diff mbox

[v3,1/9] pinctrl: mvebu: pinctrl driver core

Message ID 1347266386-16229-2-git-send-email-sebastian.hesselbarth@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sebastian Hesselbarth Sept. 10, 2012, 8:39 a.m. UTC
This patch adds a pinctrl driver core for Marvell SoCs plus DT
binding documentation. This core driver will be used by SoC family
specific drivers, i.e. Armada XP, Armada 370, Dove, Kirkwood, aso.

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
v2:
- restructured Kconfig to have a common PINCTRL_MVEBU that are selected by
  SoC specific drivers.
- cleaned pinctrl-mvebu and replaced 'magic numbers' by defines
- make use of of_iomap instead of get_resource/ioremap
- extended include documentation and checked with scripts/kernel-doc
- cleaned devicetree binding documentation

v3:
- list of functions is now built out of pin groups passed to core driver
  instead of parsing DT node.

Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.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: Lior Amsalem <alior@marvell.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Gregory CLEMENT <gregory.clement@free-electrons.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Stephen Warren <swarren@wwwdotorg.org>
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,mvebu-pinctrl.txt     |   45 ++
 arch/arm/Kconfig                                   |    1 +
 drivers/pinctrl/Kconfig                            |    6 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-mvebu.c                    |  705 ++++++++++++++++++++
 drivers/pinctrl/pinctrl-mvebu.h                    |  192 ++++++
 6 files changed, 950 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/marvell,mvebu-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-mvebu.c
 create mode 100644 drivers/pinctrl/pinctrl-mvebu.h

Comments

Linus Walleij Sept. 10, 2012, 3:39 p.m. UTC | #1
On Mon, Sep 10, 2012 at 1:39 AM, Sebastian Hesselbarth
<sebastian.hesselbarth@gmail.com> wrote:

> This patch adds a pinctrl driver core for Marvell SoCs plus DT
> binding documentation. This core driver will be used by SoC family
> specific drivers, i.e. Armada XP, Armada 370, Dove, Kirkwood, aso.
>
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> ---
> v2:
> - restructured Kconfig to have a common PINCTRL_MVEBU that are selected by
>   SoC specific drivers.
> - cleaned pinctrl-mvebu and replaced 'magic numbers' by defines
> - make use of of_iomap instead of get_resource/ioremap
> - extended include documentation and checked with scripts/kernel-doc
> - cleaned devicetree binding documentation
>
> v3:
> - list of functions is now built out of pin groups passed to core driver
>   instead of parsing DT node.

Looks good to me.
Acked-by: Linus Walleij <linus.walleij@linaro.org>

Yours,
Linus Walleij
Thomas Petazzoni Sept. 11, 2012, 2:44 p.m. UTC | #2
Hello Sebastian,

Sorry for getting back to you so late about this patch set. I have been
very busy with other things.

Le Mon, 10 Sep 2012 10:39:38 +0200,
Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> a écrit :

> v3:
> - list of functions is now built out of pin groups passed to core driver
>   instead of parsing DT node.

Even though I have gone through your discussion with Stephen Warren on
this, I don't get what you have done exactly, and I'm even more puzzled
by the following piece of code:

> +static int __devinit _add_function(const char **funcs, const char *name)
> +{
> +	int n = 0;
> +
> +	while (funcs[n]) {
> +		/* function already there */
> +		if (strcmp(funcs[n], name) == 0)
> +			return -EEXIST;
> +		n++;
> +	}
> +	funcs[n] = name;
> +	return 0;
> +}
> +
> +static int __devinit mvebu_pinctrl_build_functions(struct platform_device *pdev,
> +						   struct mvebu_pinctrl *pctl)
> +{
> +	const char **prefunc = kzalloc(sizeof(char *), GFP_KERNEL);
> +	int num = 0;
> +	int n, s;
> +
> +	for (n = 0; n < pctl->num_groups; n++) {
> +		struct mvebu_pinctrl_group *grp = &pctl->groups[n];
> +		for (s = 0; s < grp->num_settings; s++) {
> +			/* skip unsupported settings on this variant */
> +			if (pctl->variant &&
> +			    !(pctl->variant & grp->settings[s].variant))
> +				continue;
> +
> +			/* check for unique functions */
> +			if (_add_function(prefunc, grp->settings[s].name))
> +				continue;
> +
> +			num++;
> +		}
> +	}
> +	return 0;
> +}

What is this supposed to do? It allocates an array prefunc, whose
reference is only stored in a local variable, and anywhere else, so
basically it does nothing except leaking memory unless I got it wrong.

Moreover, this array has only one entry, while the loop accesses
several entries, which probably explains the crash I'm seeing at boot
time:

Unable to handle kernel paging request at virtual address ff74edca
pgd = ef290000
[ff74edca] *pgd=00000000
Internal error: Oops: 15 [#1] ARM
Modules linked in:
CPU: 0    Not tainted  (3.6.0-rc1-00021-g1dd75c4-dirty #336)
PC is at strcmp+0x0/0x30
LR is at mvebu_pinctrl_build_functions.isra.1+0x78/0xd0
pc : [<c011b600>]    lr : [<c021fbf8>]    psr: a0000013
sp : ef02de80  ip : 00000000  fp : c02a7e28
r10: 000002a0  r9 : ef0e4924  r8 : 00000018
r7 : ef2742d0  r6 : ef0cb2b0  r5 : 00000020  r4 : 00000002
r3 : ff74edca  r2 : ef0e4920  r1 : c02a7e28  r0 : ff74edca
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
Control: 10c53c7d  Table: 2f290059  DAC: 00000015
Process swapper (pid: 1, stack limit = 0xef02c268)
Stack: (0xef02de80 to 0xef02e000)
de80: ef0879c8 ef0e4900 00000043 00000043 ef2742d0 c054dd68 00000002 c057486c
dea0: 00000000 ef0879c8 ef0cb748 c02200f0 ef086348 c054c948 c055b550 c0576230
dec0: c054c6e0 ef0879c8 00000000 c054c6e0 c03008a0 c02f5c88 00000000 c014ed28
dee0: c014ed14 c014da3c ef0879c8 c054c6e0 ef0879fc 00000000 c02e91e0 c014dd30
df00: c054c6e0 c014dca4 00000000 c014c5e0 ef00554c ef0266f0 c054c6e0 c0551708
df20: ef274340 c014d380 c02a7da0 c055bf40 c054c6e0 ef02c000 c055bf40 00000000
df40: c02e91e0 c014e1e0 00000000 c02fdb4c ef02c000 c055bf40 00000000 c02e91e0
df60: c02f5c88 c00085c0 c02d1768 00000006 00000044 c02f5c88 00000044 00000006
df80: 00000006 c02e91e0 c1857e15 00000000 00000000 c02fdb4c 00000006 c02fdb2c
dfa0: c055bf40 c02e91e0 c03008a0 00000044 00000000 c02e9838 00000006 00000006
dfc0: c02e91e0 c030066c c030066c c030066c c000a04c 00000013 00000000 00000000
dfe0: 00000000 c02e98cc 00000000 00000000 c02e986c c000a04c be8ff7c8 e8dfff5e
[<c011b600>] (strcmp+0x0/0x30) from [<c021fbf8>] (mvebu_pinctrl_build_functions.isra.1+0x78/0xd0)
[<c021fbf8>] (mvebu_pinctrl_build_functions.isra.1+0x78/0xd0) from [<c02200f0>] (mvebu_pinctrl_probe+0x4a0/0x564)
[<c02200f0>] (mvebu_pinctrl_probe+0x4a0/0x564) from [<c014ed28>] (platform_drv_probe+0x14/0x18)
[<c014ed28>] (platform_drv_probe+0x14/0x18) from [<c014da3c>] (really_probe+0x60/0x1e4)
[<c014da3c>] (really_probe+0x60/0x1e4) from [<c014dd30>] (__driver_attach+0x8c/0x90)
[<c014dd30>] (__driver_attach+0x8c/0x90) from [<c014c5e0>] (bus_for_each_dev+0x50/0x7c)
[<c014c5e0>] (bus_for_each_dev+0x50/0x7c) from [<c014d380>] (bus_add_driver+0x168/0x22c)
[<c014d380>] (bus_add_driver+0x168/0x22c) from [<c014e1e0>] (driver_register+0x78/0x144)
[<c014e1e0>] (driver_register+0x78/0x144) from [<c00085c0>] (do_one_initcall+0x34/0x174)
[<c00085c0>] (do_one_initcall+0x34/0x174) from [<c02e9838>] (do_basic_setup+0x90/0xc4)
[<c02e9838>] (do_basic_setup+0x90/0xc4) from [<c02e98cc>] (kernel_init+0x60/0xf4)
[<c02e98cc>] (kernel_init+0x60/0xf4) from [<c000a04c>] (kernel_thread_exit+0x0/0x8)
Code: e3530000 e4c23001 1afffffb e12fff1e (e4d03001) 
---[ end trace a80e01be2a5f5b89 ]---
Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b

I'd like to fix that myself, but I really don't understand what the
"functions" word mean now in this v3, as compared to v1/v2 of this
patch set. Could you enlighten me on this?

Thanks!

Thomas
Stephen Warren Sept. 11, 2012, 10:17 p.m. UTC | #3
On 09/11/2012 08:44 AM, Thomas Petazzoni wrote:
> Hello Sebastian,
> 
> Sorry for getting back to you so late about this patch set. I have been
> very busy with other things.
> 
> Le Mon, 10 Sep 2012 10:39:38 +0200,
> Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> a écrit :
> 
>> v3:
>> - list of functions is now built out of pin groups passed to core driver
>>   instead of parsing DT node.
> 
> Even though I have gone through your discussion with Stephen Warren on
> this, I don't get what you have done exactly, and I'm even more puzzled
> by the following piece of code:
> 
>> +static int __devinit _add_function(const char **funcs, const char *name)
>> +{
>> +	int n = 0;
>> +
>> +	while (funcs[n]) {
>> +		/* function already there */
>> +		if (strcmp(funcs[n], name) == 0)
>> +			return -EEXIST;
>> +		n++;
>> +	}
>> +	funcs[n] = name;
>> +	return 0;
>> +}
>> +
>> +static int __devinit mvebu_pinctrl_build_functions(struct platform_device *pdev,
>> +						   struct mvebu_pinctrl *pctl)
>> +{
>> +	const char **prefunc = kzalloc(sizeof(char *), GFP_KERNEL);
>> +	int num = 0;
>> +	int n, s;
>> +
>> +	for (n = 0; n < pctl->num_groups; n++) {
>> +		struct mvebu_pinctrl_group *grp = &pctl->groups[n];
>> +		for (s = 0; s < grp->num_settings; s++) {
>> +			/* skip unsupported settings on this variant */
>> +			if (pctl->variant &&
>> +			    !(pctl->variant & grp->settings[s].variant))
>> +				continue;
>> +
>> +			/* check for unique functions */
>> +			if (_add_function(prefunc, grp->settings[s].name))
>> +				continue;
>> +
>> +			num++;
>> +		}
>> +	}
>> +	return 0;
>> +}
> 
> What is this supposed to do? It allocates an array prefunc, whose
> reference is only stored in a local variable, and anywhere else, so
> basically it does nothing except leaking memory unless I got it wrong.

I imagine this is related to the way that the SoC-specific drivers
provide their configuration to the generic core driver. I'm not sure the
data structures used for this purpose are the best design.

The pinctrl core expects lists of:

* Pins

* Groups of pins (each being a name and an array of pins in the group)

* Functions that can be muxed onto the groups (each function being a
global entity rather than something with a pin or group, and each
function being a name, and an array of groups where the function can be
selected).

However, the drivers in this patch seem to invert the data-structures a
little - in other words, instead of defining a global list of functions,
they define a list of groups, and for each group, list the functions
that can be selected on to it.

In turn, that probably requires the core mvebu driver to invert these
data-structures at run-time in order to provide the data the pinctrl
core needs. I think it'd be better to just have each SoC-specific driver
store the data tables in the same format that the pinctrl core needs it,
so that the mvebu pinctrl core won't have to process the data-structures
at all.

In particular, the following data structure is what I'm talking about:

+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)),

it's defining the functions within the context of a particular group
("mode" in the drivers terminology, I think...) rather than defining
functions and groups as separate top-level tables.
Linus Walleij Sept. 12, 2012, 6:04 a.m. UTC | #4
On Wed, Sep 12, 2012 at 12:17 AM, Stephen Warren <swarren@wwwdotorg.org> wrote:

> the drivers in this patch seem to invert the data-structures a
> little - in other words, instead of defining a global list of functions,
> they define a list of groups, and for each group, list the functions
> that can be selected on to it.
>
> In turn, that probably requires the core mvebu driver to invert these
> data-structures at run-time in order to provide the data the pinctrl
> core needs. I think it'd be better to just have each SoC-specific driver
> store the data tables in the same format that the pinctrl core needs it,
> so that the mvebu pinctrl core won't have to process the data-structures
> at all.

I sort of agree, not a blocker from my side but it'll surely make the
code easier to maintain.

Else I think the culprit function needs some commenting to be
readable, and writing in plaintext what can be explained by
simpler code is not good...

Yours,
Linus Walleij
Thomas Petazzoni Sept. 12, 2012, 6:54 a.m. UTC | #5
Le Tue, 11 Sep 2012 16:17:13 -0600,
Stephen Warren <swarren@wwwdotorg.org> a écrit :

> 
> +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)),
> 
> it's defining the functions within the context of a particular group
> ("mode" in the drivers terminology, I think...) rather than defining
> functions and groups as separate top-level tables.

This data structure really reflects what the datasheet says. Typically,
for SoCs where each pin is independently muxable (AT91, i.MX23/28,
Marvell, and probably many more), the datasheet has a big array, with
one line per pin, and then several columns which tell for a given pin,
what is "function 0", "function 1", "function 2", "function 3", etc.

So the data structure that Sebastian has implemented in the mvebu
driver immediately reflects this. In fact, the pinctrl table code for
Armada 370 and Armada XP was semi-automatically generated from CSV data
of the pinmux functions, directly coming from the datasheets. Having to
create a global list of all possible functions seems useless and
painful, since the functions only make sense in the context of one
particular pin.

Could you be more specific as to what representation you're looking
after? You're proposing to "define functions and groups as separate
top-level tables", but then how to you map which functions are
available for which group, and what is the number of that function is
this group (which we need to actually configure the mapping). I'd
really like to see (with a short code example) what is your view of how
pinmux should be handled for SoCs having independently muxable pins.

I really don't understand how a global list of functions make sense:
the functions make sense on a per-group basis, and this is how the
hardwaredatasheet defines them. If we could keep this representation,
it would really be useful.

Thanks!

Thomas
Linus Walleij Sept. 12, 2012, 3:50 p.m. UTC | #6
On Wed, Sep 12, 2012 at 8:54 AM, Thomas Petazzoni
<thomas.petazzoni@free-electrons.com> wrote:

> This data structure really reflects what the datasheet says. Typically,
> for SoCs where each pin is independently muxable (AT91, i.MX23/28,
> Marvell, and probably many more), the datasheet has a big array, with
> one line per pin, and then several columns which tell for a given pin,
> what is "function 0", "function 1", "function 2", "function 3", etc.

This is a valid reason to keep it as is in my book.
Readability along with a datasheet is important, most important
is that it has a good structure for those maintaining the driver.

Is this datasheet available so I can get the picture?

Yours,
Linus Walleij
Thomas Petazzoni Sept. 12, 2012, 4:01 p.m. UTC | #7
Le Wed, 12 Sep 2012 17:50:48 +0200,
Linus Walleij <linus.walleij@linaro.org> a écrit :

> This is a valid reason to keep it as is in my book.
> Readability along with a datasheet is important, most important
> is that it has a good structure for those maintaining the driver.
> 
> Is this datasheet available so I can get the picture?

All publicly available datasheets for Marvell ARM SoCs are referenced
in Documentation/arm/Marvell/README.

See for example
http://www.marvell.com/embedded-processors/kirkwood/assets/HW_88F6281_OpenSource.pdf,
which is the hardware datasheet for the 88F6281 Marvell SoC (Kirkwood
family). Table 26 on page 53 of the PDF is a good example. It lists all
the pins, on per row, and then has columns for each function identifier
(from 0x0 to 0x7). Then each cell says when pin X is muxed in function
Y, it provides this functionality.

So clearly, this representation is a list of pins, and for each pin, a
list of possible functions that this pin can take.

Best regards,

Thomas
Linus Walleij Sept. 12, 2012, 4:17 p.m. UTC | #8
On Wed, Sep 12, 2012 at 6:01 PM, Thomas Petazzoni
<thomas.petazzoni@free-electrons.com> wrote:

> See for example
> http://www.marvell.com/embedded-processors/kirkwood/assets/HW_88F6281_OpenSource.pdf,
> which is the hardware datasheet for the 88F6281 Marvell SoC (Kirkwood
> family). Table 26 on page 53 of the PDF is a good example. It lists all
> the pins, on per row, and then has columns for each function identifier
> (from 0x0 to 0x7). Then each cell says when pin X is muxed in function
> Y, it provides this functionality.
>
> So clearly, this representation is a list of pins, and for each pin, a
> list of possible functions that this pin can take.

OK the ux500 datasheet looks more or less the same. What I did was
to define the groups that made sense for each column (our columns are
named a,b,c), so you can see the result in
drivers/pinctrl/pinctrl-nomadik-db8500.c

In your case I would have defined groups like this:

#define PIN_MPP18 18
#define PIN_MPP19 19
(...)
static const struct pinctrl_pin_desc nmk_db8500_pins[] = {
        PINCTRL_PIN(PIN_MPP18, "MPP[18]"),
        PINCTRL_PIN(PIN_MPP19, "MPP[19]"),
(...)
};
static const unsigned mpp_1_pins[] = { PIN_MPP18, PIN_MPP19);

Then I'd register this as a group, then map the groups to
functions.

But maybe this is just stupid in your case, you'd have to tell.
(Maybe it's my driver that sucks, I don't know.)

Linus Walleij

Yours,
Linus Walleij
Stephen Warren Sept. 12, 2012, 9:10 p.m. UTC | #9
On 09/12/2012 12:54 AM, Thomas Petazzoni wrote:
> Le Tue, 11 Sep 2012 16:17:13 -0600,
> Stephen Warren <swarren@wwwdotorg.org> a écrit :
> 
>>
>> +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)),
>>
>> it's defining the functions within the context of a particular group
>> ("mode" in the drivers terminology, I think...) rather than defining
>> functions and groups as separate top-level tables.
> 
> This data structure really reflects what the datasheet says. Typically,
> for SoCs where each pin is independently muxable (AT91, i.MX23/28,
> Marvell, and probably many more), the datasheet has a big array, with
> one line per pin, and then several columns which tell for a given pin,
> what is "function 0", "function 1", "function 2", "function 3", etc.
> 
> So the data structure that Sebastian has implemented in the mvebu
> driver immediately reflects this. In fact, the pinctrl table code for
> Armada 370 and Armada XP was semi-automatically generated from CSV data
> of the pinmux functions, directly coming from the datasheets.

OK, that seems like a reasonable explanation.

Still, doing data manipulation at run-time when it could easily be done
by the script that converts your CSV into the driver tables seem
inefficient at least. I agree with LinusW that it's not a big deal though.

> Having to
> create a global list of all possible functions seems useless and
> painful, since the functions only make sense in the context of one
> particular pin.

Surely some of your functions can be selected onto 1 of n pins? If
that's true, then the functions don't only exist within the context of a
single pin.

Note that some pinctrl driver authors have decided to create functions
based on just the mux register values (e.g. mux0, mux1, mux2, mux3 for a
2-bit field) rather than semantically (e.g. uart1, uart2, i2c1, i2c2),
in which case, all functions are global and available on any pin. I
actually somewhat wonder if I shouldn't have done that for Tegra.

> Could you be more specific as to what representation you're looking
> after? You're proposing to "define functions and groups as separate
> top-level tables", but then how to you map which functions are
> available for which group,

The definition of a function is a function name (struct
pinmux_ops.get_function_name) and a list of groups onto which it can be
selected (struct pinmux_ops.get_function_groups).

The pinctrl core leaves it up to individual drivers how to represent how
to actually program a given group with a given function. ...

> and what is the number of that function is
> this group (which we need to actually configure the mapping). I'd
> really like to see (with a short code example) what is your view of how
> pinmux should be handled for SoCs having independently muxable pins.

 As an example, see drivers/pinctrl/pinctrl-tegra20.c variable
tegra20_groups[] where each group definition contains additional
information beyond the basic information that the pinctrl core requires
(which is just struct pinctrl_ops get_group_name get_group_pins) - i.e.
the global function ID that is selected by each of the 4 values you can
write into that pin's/group's register.

Incidentally, I see that tegra20_groups[] is almost exactly equivalent
to the data-structure we're talking about here; the difference is that
the Tegra driver additionally has pre-generated arrays like xio_groups[]
(the list of groups where function xio can be selected) in order to
short-circuit the run-time calculations that your driver is doing.

(I don't believe any of this discussion is affected by whether the HW
muxes at a per-pin level or per-pin-group level; Tegra30 muxes per-pin
and the driver uses the exact same data-structures as Tegra20 which
muxes per-group).
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,mvebu-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/marvell,mvebu-pinctrl.txt
new file mode 100644
index 0000000..dc419e3
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/marvell,mvebu-pinctrl.txt
@@ -0,0 +1,45 @@ 
+* Marvell SoC pinctrl core driver for mpp
+
+The pinctrl driver enables Marvell SoCs to configure the multi-purpose pins
+(mpp) to a specific function. For each SoC family there is a SoC specific
+driver using this core driver.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+A Marvell SoC pin configuration node is a node of a group of pins which can
+be used for a specific device or function. Each node requires one or more
+mpp pins or group of pins and a mpp function common to all pins.
+
+Required properties for pinctrl driver:
+- compatible: "marvell,<soc>-pinctrl"
+  Please refer to each marvell,<soc>-pinctrl.txt binding doc for supported SoCs.
+
+Required properties for pin configuration node:
+- marvell,pins: string array of mpp pins or group of pins to be muxed.
+- marvell,function: string representing a function to mux to for all
+    marvell,pins given in this pin configuration node. The function has to be
+    common for all marvell,pins. Please refer to marvell,<soc>-pinctrl.txt for
+    valid pin/pin group names and available function names for each SoC.
+
+Examples:
+
+uart1: serial@12100 {
+	compatible = "ns16550a";
+	reg = <0x12100 0x100>;
+	reg-shift = <2>;
+	interrupts = <7>;
+
+	pinctrl-0 = <&pmx_uart1_sw>;
+};
+
+pinctrl: pinctrl@d0200 {
+	compatible = "marvell,dove-pinctrl";
+	reg = <0xd0200 0x20>;
+
+	pmx_uart1_sw: pmx-uart1-sw {
+		marvell,pins = "mpp_uart1";
+		marvell,function = "uart1";
+	};
+};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a2b6f74..2eb3f6b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -574,6 +574,7 @@  config ARCH_MVEBU
 	select IRQ_DOMAIN
 	select COMMON_CLK
 	select PLAT_ORION
+	select PINCTRL
 	help
 	  Support for the Marvell SoC Family with device tree support
 
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 54e3588..20bfdc3 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -145,6 +145,12 @@  config PINCTRL_COH901
 	  COH 901 335 and COH 901 571/3. They contain 3, 5 or 7
 	  ports of 8 GPIO pins each.
 
+config PINCTRL_MVEBU
+	bool
+	depends on ARCH_MVEBU
+	select PINMUX
+	select PINCONF
+
 source "drivers/pinctrl/spear/Kconfig"
 
 endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f40b1f8..007ed32 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -29,5 +29,6 @@  obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
 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_PLAT_SPEAR)	+= spear/
diff --git a/drivers/pinctrl/pinctrl-mvebu.c b/drivers/pinctrl/pinctrl-mvebu.c
new file mode 100644
index 0000000..52b1da1
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-mvebu.c
@@ -0,0 +1,705 @@ 
+/*
+ * Marvell MVEBU pinctrl core driver
+ *
+ * Authors: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *          Thomas Petazzoni <thomas.petazzoni@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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "core.h"
+#include "pinctrl-mvebu.h"
+
+#define MPPS_PER_REG	8
+#define MPP_BITS	4
+#define MPP_MASK	0xf
+
+struct mvebu_pinctrl_function {
+	const char *name;
+	const char **groups;
+	unsigned num_groups;
+};
+
+struct mvebu_pinctrl_group {
+	const char *name;
+	struct mvebu_mpp_ctrl *ctrl;
+	struct mvebu_mpp_ctrl_setting *settings;
+	unsigned num_settings;
+	unsigned gid;
+	unsigned *pins;
+	unsigned npins;
+};
+
+struct mvebu_pinctrl {
+	struct device *dev;
+	struct pinctrl_dev *pctldev;
+	struct pinctrl_desc desc;
+	void __iomem *base;
+	struct mvebu_pinctrl_group *groups;
+	unsigned num_groups;
+	struct mvebu_pinctrl_function *functions;
+	unsigned num_functions;
+	u8 variant;
+};
+
+struct mvebu_pinctrl_group *mvebu_pinctrl_find_group_by_pid(
+	struct mvebu_pinctrl *pctl, unsigned pid)
+{
+	unsigned n;
+	for (n = 0; n < pctl->num_groups; n++) {
+		if (pid >= pctl->groups[n].pins[0] &&
+		    pid < pctl->groups[n].pins[0] +
+			pctl->groups[n].npins)
+			return &pctl->groups[n];
+	}
+	return NULL;
+}
+
+struct mvebu_pinctrl_group *mvebu_pinctrl_find_group_by_name(
+	struct mvebu_pinctrl *pctl, const char *name)
+{
+	unsigned n;
+	for (n = 0; n < pctl->num_groups; n++) {
+		if (strcmp(name, pctl->groups[n].name) == 0)
+			return &pctl->groups[n];
+	}
+	return NULL;
+}
+
+struct mvebu_mpp_ctrl_setting *mvebu_pinctrl_find_setting_by_val(
+	struct mvebu_pinctrl *pctl, struct mvebu_pinctrl_group *grp,
+	unsigned long config)
+{
+	unsigned n;
+	for (n = 0; n < grp->num_settings; n++) {
+		if (config == grp->settings[n].val) {
+			if (!pctl->variant || (pctl->variant &
+					       grp->settings[n].variant))
+				return &grp->settings[n];
+		}
+	}
+	return NULL;
+}
+
+struct mvebu_mpp_ctrl_setting *mvebu_pinctrl_find_setting_by_name(
+	struct mvebu_pinctrl *pctl, struct mvebu_pinctrl_group *grp,
+	const char *name)
+{
+	unsigned n;
+	for (n = 0; n < grp->num_settings; n++) {
+		if (strcmp(name, grp->settings[n].name) == 0) {
+			if (!pctl->variant || (pctl->variant &
+					       grp->settings[n].variant))
+				return &grp->settings[n];
+		}
+	}
+	return NULL;
+}
+
+struct mvebu_mpp_ctrl_setting *mvebu_pinctrl_find_gpio_setting(
+	struct mvebu_pinctrl *pctl, struct mvebu_pinctrl_group *grp)
+{
+	unsigned n;
+	for (n = 0; n < grp->num_settings; n++) {
+		if (grp->settings[n].flags &
+			(MVEBU_SETTING_GPO | MVEBU_SETTING_GPI)) {
+			if (!pctl->variant || (pctl->variant &
+						grp->settings[n].variant))
+				return &grp->settings[n];
+		}
+	}
+	return NULL;
+}
+
+#if 0
+struct mvebu_pinctrl_function *mvebu_pinctrl_find_function_by_name(
+	struct mvebu_pinctrl *pctl, const char *name)
+{
+	unsigned n;
+	for (n = 0; n < pctl->num_functions; n++) {
+		if (strcmp(name, pctl->functions[n].name) == 0)
+			return &pctl->functions[n];
+	}
+	return NULL;
+}
+#endif
+
+/*
+ * Common mpp pin configuration registers on MVEBU are
+ * registers of eight 4-bit values for each mpp setting.
+ * Register offset and bit mask are calculated accordingly below.
+ */
+static int mvebu_common_mpp_get(struct mvebu_pinctrl *pctl,
+				struct mvebu_pinctrl_group *grp,
+				unsigned long *config)
+{
+	unsigned pin = grp->gid;
+	unsigned off = (pin / MPPS_PER_REG) * MPP_BITS;
+	unsigned shift = (pin % MPPS_PER_REG) * MPP_BITS;
+
+	*config = readl(pctl->base + off);
+	*config >>= shift;
+	*config &= MPP_MASK;
+
+	return 0;
+}
+
+static int mvebu_common_mpp_set(struct mvebu_pinctrl *pctl,
+				struct mvebu_pinctrl_group *grp,
+				unsigned long config)
+{
+	unsigned pin = grp->gid;
+	unsigned off = (pin / MPPS_PER_REG) * MPP_BITS;
+	unsigned shift = (pin % MPPS_PER_REG) * MPP_BITS;
+	unsigned long reg;
+
+	reg = readl(pctl->base + off);
+	reg &= ~(MPP_MASK << shift);
+	reg |= (config << shift);
+	writel(reg, pctl->base + off);
+
+	return 0;
+}
+
+static int mvebu_pinconf_group_get(struct pinctrl_dev *pctldev,
+				unsigned gid, unsigned long *config)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct mvebu_pinctrl_group *grp = &pctl->groups[gid];
+
+	if (!grp->ctrl)
+		return -EINVAL;
+
+	if (grp->ctrl->mpp_get)
+		return grp->ctrl->mpp_get(grp->ctrl, config);
+
+	return mvebu_common_mpp_get(pctl, grp, config);
+}
+
+static int mvebu_pinconf_group_set(struct pinctrl_dev *pctldev,
+				unsigned gid, unsigned long config)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct mvebu_pinctrl_group *grp = &pctl->groups[gid];
+
+	if (!grp->ctrl)
+		return -EINVAL;
+
+	if (grp->ctrl->mpp_set)
+		return grp->ctrl->mpp_set(grp->ctrl, config);
+
+	return mvebu_common_mpp_set(pctl, grp, config);
+}
+
+static void mvebu_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+					struct seq_file *s, unsigned gid)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct mvebu_pinctrl_group *grp = &pctl->groups[gid];
+	struct mvebu_mpp_ctrl_setting *curr;
+	unsigned long config;
+	unsigned n;
+
+	if (mvebu_pinconf_group_get(pctldev, gid, &config))
+		return;
+
+	curr = mvebu_pinctrl_find_setting_by_val(pctl, grp, config);
+
+	if (curr) {
+		seq_printf(s, "current: %s", curr->name);
+		if (curr->subname)
+			seq_printf(s, "(%s)", curr->subname);
+		if (curr->flags & (MVEBU_SETTING_GPO | MVEBU_SETTING_GPI)) {
+			seq_printf(s, "(");
+			if (curr->flags & MVEBU_SETTING_GPI)
+				seq_printf(s, "i");
+			if (curr->flags & MVEBU_SETTING_GPO)
+				seq_printf(s, "o");
+			seq_printf(s, ")");
+		}
+	} else
+		seq_printf(s, "current: UNKNOWN");
+
+	if (grp->num_settings > 1) {
+		seq_printf(s, ", available = [");
+		for (n = 0; n < grp->num_settings; n++) {
+			if (curr == &grp->settings[n])
+				continue;
+
+			/* skip unsupported settings for this variant */
+			if (pctl->variant &&
+			    !(pctl->variant & grp->settings[n].variant))
+				continue;
+
+			seq_printf(s, " %s", grp->settings[n].name);
+			if (grp->settings[n].subname)
+				seq_printf(s, "(%s)", grp->settings[n].subname);
+			if (grp->settings[n].flags &
+				(MVEBU_SETTING_GPO | MVEBU_SETTING_GPI)) {
+				seq_printf(s, "(");
+				if (grp->settings[n].flags & MVEBU_SETTING_GPI)
+					seq_printf(s, "i");
+				if (grp->settings[n].flags & MVEBU_SETTING_GPO)
+					seq_printf(s, "o");
+				seq_printf(s, ")");
+			}
+		}
+		seq_printf(s, " ]");
+	}
+	return;
+}
+
+struct pinconf_ops mvebu_pinconf_ops = {
+	.pin_config_group_get = mvebu_pinconf_group_get,
+	.pin_config_group_set = mvebu_pinconf_group_set,
+	.pin_config_group_dbg_show = mvebu_pinconf_group_dbg_show,
+};
+
+static int mvebu_pinmux_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctl->num_functions;
+}
+
+static const char *mvebu_pinmux_get_func_name(struct pinctrl_dev *pctldev,
+					unsigned fid)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctl->functions[fid].name;
+}
+
+static int mvebu_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned fid,
+				const char * const **groups,
+				unsigned * const num_groups)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+	*groups = pctl->functions[fid].groups;
+	*num_groups = pctl->functions[fid].num_groups;
+	return 0;
+}
+
+static int mvebu_pinmux_enable(struct pinctrl_dev *pctldev, unsigned fid,
+			unsigned gid)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct mvebu_pinctrl_function *func = &pctl->functions[fid];
+	struct mvebu_pinctrl_group *grp = &pctl->groups[gid];
+	struct mvebu_mpp_ctrl_setting *setting;
+	int ret;
+
+	setting = mvebu_pinctrl_find_setting_by_name(pctl, grp,
+						     func->name);
+	if (!setting) {
+		dev_err(pctl->dev,
+			"unable to find setting %s in group %s\n",
+			func->name, func->groups[gid]);
+		return -EINVAL;
+	}
+
+	ret = mvebu_pinconf_group_set(pctldev, grp->gid, setting->val);
+	if (ret) {
+		dev_err(pctl->dev, "cannot set group %s to %s\n",
+			func->groups[gid], func->name);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int mvebu_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev,
+			struct pinctrl_gpio_range *range, unsigned offset)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct mvebu_pinctrl_group *grp;
+	struct mvebu_mpp_ctrl_setting *setting;
+
+	grp = mvebu_pinctrl_find_group_by_pid(pctl, offset);
+	if (!grp)
+		return -EINVAL;
+
+	if (grp->ctrl->mpp_gpio_req)
+		return grp->ctrl->mpp_gpio_req(grp->ctrl, offset);
+
+	setting = mvebu_pinctrl_find_gpio_setting(pctl, grp);
+	if (!setting)
+		return -ENOTSUPP;
+
+	return mvebu_pinconf_group_set(pctldev, grp->gid, setting->val);
+}
+
+static int mvebu_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
+	   struct pinctrl_gpio_range *range, unsigned offset, bool input)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct mvebu_pinctrl_group *grp;
+	struct mvebu_mpp_ctrl_setting *setting;
+
+	grp = mvebu_pinctrl_find_group_by_pid(pctl, offset);
+	if (!grp)
+		return -EINVAL;
+
+	if (grp->ctrl->mpp_gpio_dir)
+		return grp->ctrl->mpp_gpio_dir(grp->ctrl, offset, input);
+
+	setting = mvebu_pinctrl_find_gpio_setting(pctl, grp);
+	if (!setting)
+		return -ENOTSUPP;
+
+	if ((input && (setting->flags & MVEBU_SETTING_GPI)) ||
+	    (!input && (setting->flags & MVEBU_SETTING_GPO)))
+		return 0;
+
+	return -ENOTSUPP;
+}
+
+static struct pinmux_ops mvebu_pinmux_ops = {
+	.get_functions_count = mvebu_pinmux_get_funcs_count,
+	.get_function_name = mvebu_pinmux_get_func_name,
+	.get_function_groups = mvebu_pinmux_get_groups,
+	.gpio_request_enable = mvebu_pinmux_gpio_request_enable,
+	.gpio_set_direction = mvebu_pinmux_gpio_set_direction,
+	.enable = mvebu_pinmux_enable,
+};
+
+static int mvebu_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	return pctl->num_groups;
+}
+
+static const char *mvebu_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+						unsigned gid)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	return pctl->groups[gid].name;
+}
+
+static int mvebu_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+					unsigned gid, const unsigned **pins,
+					unsigned *num_pins)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	*pins = pctl->groups[gid].pins;
+	*num_pins = pctl->groups[gid].npins;
+	return 0;
+}
+
+static int mvebu_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+					struct device_node *np,
+					struct pinctrl_map **map,
+					unsigned *num_maps)
+{
+	struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct property *prop;
+	const char *function;
+	const char *group;
+	int ret, nmaps, n;
+
+	*map = NULL;
+	*num_maps = 0;
+
+	ret = of_property_read_string(np, "marvell,function", &function);
+	if (ret) {
+		dev_err(pctl->dev,
+			"missing marvell,function in node %s\n", np->name);
+		return 0;
+	}
+
+	nmaps = of_property_count_strings(np, "marvell,pins");
+	if (nmaps < 0) {
+		dev_err(pctl->dev,
+			"missing marvell,pins in node %s\n", np->name);
+		return 0;
+	}
+
+	*map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL);
+	if (map == NULL) {
+		dev_err(pctl->dev,
+			"cannot allocate pinctrl_map memory for %s\n",
+			np->name);
+		return -ENOMEM;
+	}
+
+	n = 0;
+	of_property_for_each_string(np, "marvell,pins", prop, group) {
+		struct mvebu_pinctrl_group *grp =
+			mvebu_pinctrl_find_group_by_name(pctl, group);
+
+		if (!grp) {
+			dev_err(pctl->dev, "unknown pin %s", group);
+			continue;
+		}
+
+		if (!mvebu_pinctrl_find_setting_by_name(pctl, grp, function)) {
+			dev_err(pctl->dev, "unsupported function %s on pin %s",
+				function, group);
+			continue;
+		}
+
+		(*map)[n].type = PIN_MAP_TYPE_MUX_GROUP;
+		(*map)[n].data.mux.group = group;
+		(*map)[n].data.mux.function = np->name;
+		n++;
+	}
+
+	*num_maps = nmaps;
+
+	return 0;
+}
+
+static void mvebu_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,
+				struct pinctrl_map *map, unsigned num_maps)
+{
+	kfree(map);
+}
+
+static struct pinctrl_ops mvebu_pinctrl_ops = {
+	.get_groups_count = mvebu_pinctrl_get_groups_count,
+	.get_group_name = mvebu_pinctrl_get_group_name,
+	.get_group_pins = mvebu_pinctrl_get_group_pins,
+	.dt_node_to_map = mvebu_pinctrl_dt_node_to_map,
+	.dt_free_map = mvebu_pinctrl_dt_free_map,
+};
+
+static int __devinit _add_function(const char **funcs, const char *name)
+{
+	int n = 0;
+
+	while (funcs[n]) {
+		/* function already there */
+		if (strcmp(funcs[n], name) == 0)
+			return -EEXIST;
+		n++;
+	}
+	funcs[n] = name;
+	return 0;
+}
+
+static int __devinit mvebu_pinctrl_build_functions(struct platform_device *pdev,
+						   struct mvebu_pinctrl *pctl)
+{
+	const char **prefunc = kzalloc(sizeof(char *), GFP_KERNEL);
+	int num = 0;
+	int n, s;
+
+	for (n = 0; n < pctl->num_groups; n++) {
+		struct mvebu_pinctrl_group *grp = &pctl->groups[n];
+		for (s = 0; s < grp->num_settings; s++) {
+			/* skip unsupported settings on this variant */
+			if (pctl->variant &&
+			    !(pctl->variant & grp->settings[s].variant))
+				continue;
+
+			/* check for unique functions */
+			if (_add_function(prefunc, grp->settings[s].name))
+				continue;
+
+			num++;
+		}
+	}
+	return 0;
+}
+
+int __devinit mvebu_pinctrl_probe(struct platform_device *pdev)
+{
+	struct mvebu_pinctrl_soc_info *soc = dev_get_platdata(&pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
+	struct mvebu_pinctrl *pctl;
+	void __iomem *base;
+	struct pinctrl_pin_desc *pdesc;
+	unsigned gid, n, k;
+	int ret;
+
+	if (!soc || !soc->controls || !soc->modes) {
+		dev_err(&pdev->dev, "wrong pinctrl soc info\n");
+		return -EINVAL;
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		dev_err(&pdev->dev, "unable to get base address\n");
+		return -ENODEV;
+	}
+
+	pctl = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pinctrl),
+			GFP_KERNEL);
+	if (!pctl) {
+		dev_err(&pdev->dev, "unable to alloc driver\n");
+		return -ENOMEM;
+	}
+
+	pctl->desc.name = dev_name(&pdev->dev);
+	pctl->desc.owner = THIS_MODULE;
+	pctl->desc.pctlops = &mvebu_pinctrl_ops;
+	pctl->desc.pmxops = &mvebu_pinmux_ops;
+	pctl->desc.confops = &mvebu_pinconf_ops;
+	pctl->variant = soc->variant;
+	pctl->base = base;
+	pctl->dev = &pdev->dev;
+	platform_set_drvdata(pdev, pctl);
+
+	/* count controls and create names for mvebu generic
+	   register controls; also does sanity checks */
+	pctl->num_groups = 0;
+	pctl->desc.npins = 0;
+	for (n = 0; n < soc->ncontrols; n++) {
+		struct mvebu_mpp_ctrl *ctrl = &soc->controls[n];
+		char *names;
+
+		pctl->desc.npins += ctrl->npins;
+		/* initial control pins */
+		for (k = 0; k < ctrl->npins; k++)
+			ctrl->pins[k] = ctrl->pid + k;
+
+		/* special soc specific control */
+		if (ctrl->mpp_get || ctrl->mpp_set) {
+			if (!ctrl->name || !ctrl->mpp_set || !ctrl->mpp_set) {
+				dev_err(&pdev->dev, "wrong soc control info\n");
+				return -EINVAL;
+			}
+			pctl->num_groups += 1;
+			continue;
+		}
+
+		/* generic mvebu register control */
+		names = devm_kzalloc(&pdev->dev, ctrl->npins * 8, GFP_KERNEL);
+		if (!names) {
+			dev_err(&pdev->dev, "failed to alloc mpp names\n");
+			return -ENOMEM;
+		}
+		for (k = 0; k < ctrl->npins; k++)
+			sprintf(names + 8*k, "mpp%d", ctrl->pid+k);
+		ctrl->name = names;
+		pctl->num_groups += ctrl->npins;
+	}
+
+	pdesc = devm_kzalloc(&pdev->dev, pctl->desc.npins *
+			     sizeof(struct pinctrl_pin_desc), GFP_KERNEL);
+	if (!pdesc) {
+		dev_err(&pdev->dev, "failed to alloc pinctrl pins\n");
+		return -ENOMEM;
+	}
+
+	for (n = 0; n < pctl->desc.npins; n++)
+		pdesc[n].number = n;
+	pctl->desc.pins = pdesc;
+
+	pctl->groups = devm_kzalloc(&pdev->dev, pctl->num_groups *
+			     sizeof(struct mvebu_pinctrl_group), GFP_KERNEL);
+	if (!pctl->groups) {
+		dev_err(&pdev->dev, "failed to alloc pinctrl groups\n");
+		return -ENOMEM;
+	}
+
+	/* assign mpp controls to groups */
+	gid = 0;
+	for (n = 0; n < soc->ncontrols; n++) {
+		struct mvebu_mpp_ctrl *ctrl = &soc->controls[n];
+		pctl->groups[gid].gid = gid;
+		pctl->groups[gid].ctrl = ctrl;
+		pctl->groups[gid].name = ctrl->name;
+		pctl->groups[gid].pins = ctrl->pins;
+		pctl->groups[gid].npins = ctrl->npins;
+
+		/* generic mvebu register control maps to a number of groups */
+		if (!ctrl->mpp_get && !ctrl->mpp_set) {
+			pctl->groups[gid].npins = 1;
+
+			for (k = 1; k < ctrl->npins; k++) {
+				gid++;
+				pctl->groups[gid].gid = gid;
+				pctl->groups[gid].ctrl = ctrl;
+				pctl->groups[gid].name = &ctrl->name[8*k];
+				pctl->groups[gid].pins = &ctrl->pins[k];
+				pctl->groups[gid].npins = 1;
+			}
+		}
+		gid++;
+	}
+
+	/* assign mpp modes to groups */
+	for (n = 0; n < soc->nmodes; n++) {
+		struct mvebu_mpp_mode *mode = &soc->modes[n];
+		struct mvebu_pinctrl_group *grp =
+			mvebu_pinctrl_find_group_by_pid(pctl, mode->pid);
+		unsigned num_settings;
+
+		if (!grp) {
+			dev_warn(&pdev->dev, "unknown pinctrl group %d\n",
+				mode->pid);
+			continue;
+		}
+
+		for (num_settings = 0; ;) {
+			struct mvebu_mpp_ctrl_setting *set =
+				&mode->settings[num_settings];
+
+			if (!set->name)
+				break;
+			num_settings++;
+
+			/* skip unsupported settings for this variant */
+			if (pctl->variant && !(pctl->variant & set->variant))
+				continue;
+
+			/* find gpio/gpo/gpi settings */
+			if (strcmp(set->name, "gpio") == 0)
+				set->flags = MVEBU_SETTING_GPI |
+					MVEBU_SETTING_GPO;
+			else if (strcmp(set->name, "gpo") == 0)
+				set->flags = MVEBU_SETTING_GPO;
+			else if (strcmp(set->name, "gpi") == 0)
+				set->flags = MVEBU_SETTING_GPI;
+		}
+
+		grp->settings = mode->settings;
+		grp->num_settings = num_settings;
+	}
+
+	ret = mvebu_pinctrl_build_functions(pdev, pctl);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to build functions\n");
+		return ret;
+	}
+
+	pctl->pctldev = pinctrl_register(&pctl->desc, &pdev->dev, pctl);
+	if (!pctl->pctldev) {
+		dev_err(&pdev->dev, "unable to register pinctrl driver\n");
+		return -EINVAL;
+	}
+
+	dev_info(&pdev->dev, "registered pinctrl driver\n");
+
+	/* register gpio ranges */
+	for (n = 0; n < soc->ngpioranges; n++)
+		pinctrl_add_gpio_range(pctl->pctldev, &soc->gpioranges[n]);
+
+	return 0;
+}
+
+int __devexit mvebu_pinctrl_remove(struct platform_device *pdev)
+{
+	struct mvebu_pinctrl *pctl = platform_get_drvdata(pdev);
+	pinctrl_unregister(pctl->pctldev);
+	return 0;
+}
diff --git a/drivers/pinctrl/pinctrl-mvebu.h b/drivers/pinctrl/pinctrl-mvebu.h
new file mode 100644
index 0000000..90bd3be
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-mvebu.h
@@ -0,0 +1,192 @@ 
+/*
+ * Marvell MVEBU pinctrl driver
+ *
+ * Authors: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *          Thomas Petazzoni <thomas.petazzoni@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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PINCTRL_MVEBU_H__
+#define __PINCTRL_MVEBU_H__
+
+/**
+ * struct mvebu_mpp_ctrl - describe a mpp control
+ * @name: name of the control group
+ * @pid: first pin id handled by this control
+ * @npins: number of pins controlled by this control
+ * @mpp_get: (optional) special function to get mpp setting
+ * @mpp_set: (optional) special function to set mpp setting
+ * @mpp_gpio_req: (optional) special function to request gpio
+ * @mpp_gpio_dir: (optional) special function to set gpio direction
+ *
+ * A mpp_ctrl describes a muxable unit, e.g. pin, group of pins, or
+ * internal function, inside the SoC. Each muxable unit can be switched
+ * between two or more different settings, e.g. assign mpp pin 13 to
+ * uart1 or sata.
+ *
+ * If optional mpp_get/_set functions are set these are used to get/set
+ * a specific mode. Otherwise it is assumed that the mpp control is based
+ * on 4-bit groups in subsequent registers. The optional mpp_gpio_req/_dir
+ * functions can be used to allow pin settings with varying gpio pins.
+ */
+struct mvebu_mpp_ctrl {
+	const char *name;
+	u8 pid;
+	u8 npins;
+	unsigned *pins;
+	int (*mpp_get)(struct mvebu_mpp_ctrl *ctrl, unsigned long *config);
+	int (*mpp_set)(struct mvebu_mpp_ctrl *ctrl, unsigned long config);
+	int (*mpp_gpio_req)(struct mvebu_mpp_ctrl *ctrl, u8 pid);
+	int (*mpp_gpio_dir)(struct mvebu_mpp_ctrl *ctrl, u8 pid, bool input);
+};
+
+/**
+ * struct mvebu_mpp_ctrl_setting - describe a mpp ctrl setting
+ * @val: ctrl setting value
+ * @name: ctrl setting name, e.g. uart2, spi0 - unique per mpp_mode
+ * @subname: (optional) additional ctrl setting name, e.g. rts, cts
+ * @variant: (optional) variant identifier mask
+ * @flags: (private) flags to store gpi/gpo/gpio capabilities
+ *
+ * A ctrl_setting describes a specific internal mux function that a mpp pin
+ * can be switched to. The value (val) will be written in the corresponding
+ * register for common mpp pin configuration registers on MVEBU. SoC specific
+ * mpp_get/_set function may use val to distinguish between different settings.
+ *
+ * The name will be used to switch to this setting in DT description, e.g.
+ * marvell,function = "uart2". subname is only for debugging purposes.
+ *
+ * If name is one of "gpi", "gpo", "gpio" gpio capabilities are
+ * parsed during initialization and stored in flags.
+ *
+ * The variant can be used to combine different revisions of one SoC to a
+ * common pinctrl driver. It is matched (AND) with variant of soc_info to
+ * determine if a setting is available on the current SoC revision.
+ */
+struct mvebu_mpp_ctrl_setting {
+	u8 val;
+	const char *name;
+	const char *subname;
+	u8 variant;
+	u8 flags;
+#define  MVEBU_SETTING_GPO	(1 << 0)
+#define  MVEBU_SETTING_GPI	(1 << 1)
+};
+
+/**
+ * struct mvebu_mpp_mode - link ctrl and settings
+ * @pid: first pin id handled by this mode
+ * @settings: list of settings available for this mode
+ *
+ * A mode connects all available settings with the corresponding mpp_ctrl
+ * given by pid.
+ */
+struct mvebu_mpp_mode {
+	u8 pid;
+	struct mvebu_mpp_ctrl_setting *settings;
+};
+
+/**
+ * struct mvebu_pinctrl_soc_info - SoC specific info passed to pinctrl-mvebu
+ * @variant: variant mask of soc_info
+ * @controls: list of available mvebu_mpp_ctrls
+ * @ncontrols: number of available mvebu_mpp_ctrls
+ * @modes: list of available mvebu_mpp_modes
+ * @nmodes: number of available mvebu_mpp_modes
+ * @gpioranges: list of pinctrl_gpio_ranges
+ * @ngpioranges: number of available pinctrl_gpio_ranges
+ *
+ * This struct describes all pinctrl related information for a specific SoC.
+ * If variant is unequal 0 it will be matched (AND) with variant of each
+ * setting and allows to distinguish between different revisions of one SoC.
+ */
+struct mvebu_pinctrl_soc_info {
+	u8 variant;
+	struct mvebu_mpp_ctrl *controls;
+	int ncontrols;
+	struct mvebu_mpp_mode *modes;
+	int nmodes;
+	struct pinctrl_gpio_range *gpioranges;
+	int ngpioranges;
+};
+
+#define MPP_REG_CTRL(_idl, _idh)				\
+	{							\
+		.name = NULL,					\
+		.pid = _idl,					\
+		.npins = _idh - _idl + 1,			\
+		.pins = (unsigned[_idh - _idl + 1]) { },	\
+		.mpp_get = NULL,				\
+		.mpp_set = NULL,				\
+		.mpp_gpio_req = NULL,				\
+		.mpp_gpio_dir = NULL,				\
+	}
+
+#define MPP_FUNC_CTRL(_idl, _idh, _name, _func)			\
+	{							\
+		.name = _name,					\
+		.pid = _idl,					\
+		.npins = _idh - _idl + 1,			\
+		.pins = (unsigned[_idh - _idl + 1]) { },	\
+		.mpp_get = _func ## _get,			\
+		.mpp_set = _func ## _set,			\
+		.mpp_gpio_req = NULL,				\
+		.mpp_gpio_dir = NULL,				\
+	}
+
+#define MPP_FUNC_GPIO_CTRL(_idl, _idh, _name, _func)		\
+	{							\
+		.name = _name,					\
+		.pid = _idl,					\
+		.npins = _idh - _idl + 1,			\
+		.pins = (unsigned[_idh - _idl + 1]) { },	\
+		.mpp_get = _func ## _get,			\
+		.mpp_set = _func ## _set,			\
+		.mpp_gpio_req = _func ## _gpio_req,		\
+		.mpp_gpio_dir = _func ## _gpio_dir,		\
+	}
+
+#define _MPP_VAR_FUNCTION(_val, _name, _subname, _mask)		\
+	{							\
+		.val = _val,					\
+		.name = _name,					\
+		.subname = _subname,				\
+		.variant = _mask,				\
+		.flags = 0,					\
+	}
+
+#if defined(CONFIG_DEBUG_FS)
+#define MPP_VAR_FUNCTION(_val, _name, _subname, _mask)		\
+	_MPP_VAR_FUNCTION(_val, _name, _subname, _mask)
+#else
+#define MPP_VAR_FUNCTION(_val, _name, _subname, _mask)		\
+	_MPP_VAR_FUNCTION(_val, _name, NULL, _mask)
+#endif
+
+#define MPP_FUNCTION(_val, _name, _subname)			\
+	MPP_VAR_FUNCTION(_val, _name, _subname, (u8)-1)
+
+#define MPP_MODE(_id, ...)					\
+	{							\
+		.pid = _id,					\
+		.settings = (struct mvebu_mpp_ctrl_setting[]){	\
+			__VA_ARGS__, { } },			\
+	}
+
+#define MPP_GPIO_RANGE(_id, _pinbase, _gpiobase, _npins)	\
+	{							\
+		.name = "mvebu-gpio",				\
+		.id = _id,					\
+		.pin_base = _pinbase,				\
+		.base = _gpiobase,				\
+		.npins = _npins,				\
+	}
+
+int mvebu_pinctrl_probe(struct platform_device *pdev);
+int mvebu_pinctrl_remove(struct platform_device *pdev);
+
+#endif