diff mbox series

[2/3] pinctrl: ralink: add a pinctrl driver for the rt2880 family

Message ID 20201207192104.6046-3-sergio.paracuellos@gmail.com
State New
Headers show
Series pinctrl: ralink: pinctrl driver for the rt2880 family | expand

Commit Message

Sergio Paracuellos Dec. 7, 2020, 7:21 p.m. UTC
These Socs have 1-3 banks of 8-32 gpios. Rather then setting the muxing of each
pin individually, these socs have mux groups that when set will effect 1-N pins.
Pin groups have a 2, 4 or 8 different muxes.

Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com>
---
 drivers/pinctrl/Kconfig          |   6 +
 drivers/pinctrl/Makefile         |   1 +
 drivers/pinctrl/pinctrl-rt2880.c | 370 +++++++++++++++++++++++++++++++
 3 files changed, 377 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-rt2880.c

Comments

Linus Walleij Dec. 7, 2020, 11 p.m. UTC | #1
Hi Serigio,

I dug around some to try to understand the patch I think I get
it now :)

Squash this with the third patch so it becomes a
"move" of this file, preserving history. With that:
Acked-by: Linus Walleij <linus.walleij@linaro.org>

I have ideas, but it is better to move the driver out
of staging and improve it in pinctrl.

Since there might be many sub-SoCs for this pin
controller, what about creating
drivers/pinctrl/ralink/* for this at the same time?

On Mon, Dec 7, 2020 at 8:21 PM Sergio Paracuellos
<sergio.paracuellos@gmail.com> wrote:
>
> These Socs have 1-3 banks of 8-32 gpios. Rather then setting the muxing of each
> pin individually, these socs have mux groups that when set will effect 1-N pins.
> Pin groups have a 2, 4 or 8 different muxes.
>
> Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com>
(...)
> +#include <asm/mach-ralink/ralink_regs.h>
> +#include <asm/mach-ralink/pinmux.h>
> +#include <asm/mach-ralink/mt7620.h>

I think in the next step we should move the contents of
rt2880_pinmux_data into this driver, then we can drop these
mach-headers and show the way for the rest of the ralink
chips to push their data down into this driver (or subdrivers)
and depopulate mach-ralink a bit.

> +       p->groups = rt2880_pinmux_data;

So this is where the driver actually gets a pointer to all
groups and functions, and these groups and functions all
come from arch/mips/ralink/mt7621.c right?

I think after this first step we should move mt7621.c
to pinctrl and become a subdriver for this pin controller
and then we can hopefully move the rest as well once
you set the pattern for how we do this.

Yours,
Linus Walleij
Sergio Paracuellos Dec. 8, 2020, 6:53 a.m. UTC | #2
Hi Linus,

On Tue, Dec 8, 2020 at 12:00 AM Linus Walleij <linus.walleij@linaro.org> wrote:
>

> Hi Serigio,

>

> I dug around some to try to understand the patch I think I get

> it now :)

>

> Squash this with the third patch so it becomes a

> "move" of this file, preserving history. With that:

> Acked-by: Linus Walleij <linus.walleij@linaro.org>


Ok, will squash those two if you prefer that way with your 'Acked-by'.

>

> I have ideas, but it is better to move the driver out

> of staging and improve it in pinctrl.

>

> Since there might be many sub-SoCs for this pin

> controller, what about creating

> drivers/pinctrl/ralink/* for this at the same time?


Ok, I will put this inside a ralink subdirectory in pinctrl.

>

> On Mon, Dec 7, 2020 at 8:21 PM Sergio Paracuellos

> <sergio.paracuellos@gmail.com> wrote:

> >

> > These Socs have 1-3 banks of 8-32 gpios. Rather then setting the muxing of each

> > pin individually, these socs have mux groups that when set will effect 1-N pins.

> > Pin groups have a 2, 4 or 8 different muxes.

> >

> > Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com>

> (...)

> > +#include <asm/mach-ralink/ralink_regs.h>

> > +#include <asm/mach-ralink/pinmux.h>

> > +#include <asm/mach-ralink/mt7620.h>

>

> I think in the next step we should move the contents of

> rt2880_pinmux_data into this driver, then we can drop these

> mach-headers and show the way for the rest of the ralink

> chips to push their data down into this driver (or subdrivers)

> and depopulate mach-ralink a bit.


Agree. Doing that no arch dependencies are included and we can cleanly
enable the driver also for COMPILE_TEST without adding special flags
in pinctrl Makefile.

>

> > +       p->groups = rt2880_pinmux_data;

>

> So this is where the driver actually gets a pointer to all

> groups and functions, and these groups and functions all

> come from arch/mips/ralink/mt7621.c right?


Yes, all of that is defined there.

>

> I think after this first step we should move mt7621.c

> to pinctrl and become a subdriver for this pin controller

> and then we can hopefully move the rest as well once

> you set the pattern for how we do this.


I see. Thanks for advices.

>

> Yours,

> Linus Walleij


Best regards,
    Sergio Paracuellos
Dan Carpenter Dec. 8, 2020, 10:17 a.m. UTC | #3
On Mon, Dec 07, 2020 at 08:21:03PM +0100, Sergio Paracuellos wrote:
> +static struct pinctrl_desc rt2880_pctrl_desc = {

> +	.owner		= THIS_MODULE,

> +	.name		= "rt2880-pinmux",

> +	.pctlops	= &rt2880_pctrl_ops,

> +	.pmxops		= &rt2880_pmx_group_ops,

> +};

> +

> +static struct rt2880_pmx_func gpio_func = {

> +	.name = "gpio",

> +};

> +

> +static int rt2880_pinmux_index(struct rt2880_priv *p)



This function name is not great.  I assumed that it would return the
index.

> +{

> +	struct rt2880_pmx_func **f;


Get rid of this "f" variable and use "p->func" instead.

> +	struct rt2880_pmx_group *mux = p->groups;

> +	int i, j, c = 0;

> +

> +	/* count the mux functions */

> +	while (mux->name) {

> +		p->group_count++;

> +		mux++;

> +	}

> +

> +	/* allocate the group names array needed by the gpio function */

> +	p->group_names = devm_kcalloc(p->dev, p->group_count,

> +				      sizeof(char *), GFP_KERNEL);

> +	if (!p->group_names)

> +		return -1;


Return proper error codes in this function.  s/-1/-ENOMEM/

> +

> +	for (i = 0; i < p->group_count; i++) {

> +		p->group_names[i] = p->groups[i].name;

> +		p->func_count += p->groups[i].func_count;

> +	}

> +

> +	/* we have a dummy function[0] for gpio */

> +	p->func_count++;

> +

> +	/* allocate our function and group mapping index buffers */

> +	f = p->func = devm_kcalloc(p->dev,

> +				   p->func_count,

> +				   sizeof(*p->func),

> +				   GFP_KERNEL);

> +	gpio_func.groups = devm_kcalloc(p->dev, p->group_count, sizeof(int),

> +					GFP_KERNEL);

> +	if (!f || !gpio_func.groups)

> +		return -1;

> +

> +	/* add a backpointer to the function so it knows its group */

> +	gpio_func.group_count = p->group_count;

> +	for (i = 0; i < gpio_func.group_count; i++)

> +		gpio_func.groups[i] = i;

> +

> +	f[c] = &gpio_func;

> +	c++;

> +

> +	/* add remaining functions */

> +	for (i = 0; i < p->group_count; i++) {

> +		for (j = 0; j < p->groups[i].func_count; j++) {

> +			f[c] = &p->groups[i].func[j];

> +			f[c]->groups = devm_kzalloc(p->dev, sizeof(int),

> +						    GFP_KERNEL);


Add a NULL check.

> +			f[c]->groups[0] = i;

> +			f[c]->group_count = 1;

> +			c++;

> +		}

> +	}

> +	return 0;

> +}

> +

> +static int rt2880_pinmux_pins(struct rt2880_priv *p)

> +{

> +	int i, j;

> +

> +	/*

> +	 * loop over the functions and initialize the pins array.

> +	 * also work out the highest pin used.

> +	 */

> +	for (i = 0; i < p->func_count; i++) {

> +		int pin;

> +

> +		if (!p->func[i]->pin_count)

> +			continue;

> +

> +		p->func[i]->pins = devm_kcalloc(p->dev,

> +						p->func[i]->pin_count,

> +						sizeof(int),

> +						GFP_KERNEL);


This can fit on two lines.

		p->func[i]->pins = devm_kcalloc(p->dev, p->func[i]->pin_count,
						sizeof(int), GFP_KERNEL);

> +		for (j = 0; j < p->func[i]->pin_count; j++)

> +			p->func[i]->pins[j] = p->func[i]->pin_first + j;

> +

> +		pin = p->func[i]->pin_first + p->func[i]->pin_count;

> +		if (pin > p->max_pins)

> +			p->max_pins = pin;

> +	}

> +

> +	/* the buffer that tells us which pins are gpio */

> +	p->gpio = devm_kcalloc(p->dev, p->max_pins, sizeof(u8), GFP_KERNEL);

> +	/* the pads needed to tell pinctrl about our pins */

> +	p->pads = devm_kcalloc(p->dev, p->max_pins,

> +			       sizeof(struct pinctrl_pin_desc), GFP_KERNEL);

> +	if (!p->pads || !p->gpio) {

> +		dev_err(p->dev, "Failed to allocate gpio data\n");


Delete this error message.  #checkpatch.pl

> +		return -ENOMEM;

> +	}

> +

> +	memset(p->gpio, 1, sizeof(u8) * p->max_pins);

> +	for (i = 0; i < p->func_count; i++) {

> +		if (!p->func[i]->pin_count)

> +			continue;

> +

> +		for (j = 0; j < p->func[i]->pin_count; j++)

> +			p->gpio[p->func[i]->pins[j]] = 0;

> +	}

> +

> +	/* pin 0 is always a gpio */

> +	p->gpio[0] = 1;

> +

> +	/* set the pads */

> +	for (i = 0; i < p->max_pins; i++) {

> +		/* strlen("ioXY") + 1 = 5 */

> +		char *name = devm_kzalloc(p->dev, 5, GFP_KERNEL);

> +


		char *name;

		name = kasprintf(GFP_KERNEL, "io%d", i);


> +		if (!name)

> +			return -ENOMEM;

> +		snprintf(name, 5, "io%d", i);

> +		p->pads[i].number = i;

> +		p->pads[i].name = name;

> +	}

> +	p->desc->pins = p->pads;

> +	p->desc->npins = p->max_pins;

> +

> +	return 0;

> +}

> +

> +static int rt2880_pinmux_probe(struct platform_device *pdev)

> +{

> +	struct rt2880_priv *p;

> +	struct pinctrl_dev *dev;

> +

> +	if (!rt2880_pinmux_data)

> +		return -ENOTSUPP;

> +

> +	/* setup the private data */

> +	p = devm_kzalloc(&pdev->dev, sizeof(struct rt2880_priv), GFP_KERNEL);

> +	if (!p)

> +		return -ENOMEM;

> +

> +	p->dev = &pdev->dev;

> +	p->desc = &rt2880_pctrl_desc;

> +	p->groups = rt2880_pinmux_data;

> +	platform_set_drvdata(pdev, p);

> +

> +	/* init the device */

> +	if (rt2880_pinmux_index(p)) {

> +		dev_err(&pdev->dev, "failed to load index\n");

> +		return -EINVAL;


Preserve the error code:

	err = rt2880_pinmux_index(p);
	if (err) {
		dev_err(&pdev->dev, "failed to load index\n");
		return err;
	}


> +	}

> +	if (rt2880_pinmux_pins(p)) {

> +		dev_err(&pdev->dev, "failed to load pins\n");

> +		return -EINVAL;


Same.

> +	}

> +	dev = pinctrl_register(p->desc, &pdev->dev, p);

> +	if (IS_ERR(dev))

> +		return PTR_ERR(dev);

> +

> +	return 0;

> +}


regards,
dan carpenter
Sergio Paracuellos Dec. 8, 2020, 11:48 a.m. UTC | #4
Hi Dan,

Thanks for the review.

On Tue, Dec 8, 2020 at 11:17 AM Dan Carpenter <dan.carpenter@oracle.com> wrote:
>
> On Mon, Dec 07, 2020 at 08:21:03PM +0100, Sergio Paracuellos wrote:
> > +static struct pinctrl_desc rt2880_pctrl_desc = {
> > +     .owner          = THIS_MODULE,
> > +     .name           = "rt2880-pinmux",
> > +     .pctlops        = &rt2880_pctrl_ops,
> > +     .pmxops         = &rt2880_pmx_group_ops,
> > +};
> > +
> > +static struct rt2880_pmx_func gpio_func = {
> > +     .name = "gpio",
> > +};
> > +
> > +static int rt2880_pinmux_index(struct rt2880_priv *p)
>
>
> This function name is not great.  I assumed that it would return the
> index.
>
> > +{
> > +     struct rt2880_pmx_func **f;
>
> Get rid of this "f" variable and use "p->func" instead.
>
> > +     struct rt2880_pmx_group *mux = p->groups;
> > +     int i, j, c = 0;
> > +
> > +     /* count the mux functions */
> > +     while (mux->name) {
> > +             p->group_count++;
> > +             mux++;
> > +     }
> > +
> > +     /* allocate the group names array needed by the gpio function */
> > +     p->group_names = devm_kcalloc(p->dev, p->group_count,
> > +                                   sizeof(char *), GFP_KERNEL);
> > +     if (!p->group_names)
> > +             return -1;
>
> Return proper error codes in this function.  s/-1/-ENOMEM/
>
> > +
> > +     for (i = 0; i < p->group_count; i++) {
> > +             p->group_names[i] = p->groups[i].name;
> > +             p->func_count += p->groups[i].func_count;
> > +     }
> > +
> > +     /* we have a dummy function[0] for gpio */
> > +     p->func_count++;
> > +
> > +     /* allocate our function and group mapping index buffers */
> > +     f = p->func = devm_kcalloc(p->dev,
> > +                                p->func_count,
> > +                                sizeof(*p->func),
> > +                                GFP_KERNEL);
> > +     gpio_func.groups = devm_kcalloc(p->dev, p->group_count, sizeof(int),
> > +                                     GFP_KERNEL);
> > +     if (!f || !gpio_func.groups)
> > +             return -1;
> > +
> > +     /* add a backpointer to the function so it knows its group */
> > +     gpio_func.group_count = p->group_count;
> > +     for (i = 0; i < gpio_func.group_count; i++)
> > +             gpio_func.groups[i] = i;
> > +
> > +     f[c] = &gpio_func;
> > +     c++;
> > +
> > +     /* add remaining functions */
> > +     for (i = 0; i < p->group_count; i++) {
> > +             for (j = 0; j < p->groups[i].func_count; j++) {
> > +                     f[c] = &p->groups[i].func[j];
> > +                     f[c]->groups = devm_kzalloc(p->dev, sizeof(int),
> > +                                                 GFP_KERNEL);
>
> Add a NULL check.
>
> > +                     f[c]->groups[0] = i;
> > +                     f[c]->group_count = 1;
> > +                     c++;
> > +             }
> > +     }
> > +     return 0;
> > +}
> > +
> > +static int rt2880_pinmux_pins(struct rt2880_priv *p)
> > +{
> > +     int i, j;
> > +
> > +     /*
> > +      * loop over the functions and initialize the pins array.
> > +      * also work out the highest pin used.
> > +      */
> > +     for (i = 0; i < p->func_count; i++) {
> > +             int pin;
> > +
> > +             if (!p->func[i]->pin_count)
> > +                     continue;
> > +
> > +             p->func[i]->pins = devm_kcalloc(p->dev,
> > +                                             p->func[i]->pin_count,
> > +                                             sizeof(int),
> > +                                             GFP_KERNEL);
>
> This can fit on two lines.
>
>                 p->func[i]->pins = devm_kcalloc(p->dev, p->func[i]->pin_count,
>                                                 sizeof(int), GFP_KERNEL);
>
> > +             for (j = 0; j < p->func[i]->pin_count; j++)
> > +                     p->func[i]->pins[j] = p->func[i]->pin_first + j;
> > +
> > +             pin = p->func[i]->pin_first + p->func[i]->pin_count;
> > +             if (pin > p->max_pins)
> > +                     p->max_pins = pin;
> > +     }
> > +
> > +     /* the buffer that tells us which pins are gpio */
> > +     p->gpio = devm_kcalloc(p->dev, p->max_pins, sizeof(u8), GFP_KERNEL);
> > +     /* the pads needed to tell pinctrl about our pins */
> > +     p->pads = devm_kcalloc(p->dev, p->max_pins,
> > +                            sizeof(struct pinctrl_pin_desc), GFP_KERNEL);
> > +     if (!p->pads || !p->gpio) {
> > +             dev_err(p->dev, "Failed to allocate gpio data\n");
>
> Delete this error message.  #checkpatch.pl
>
> > +             return -ENOMEM;
> > +     }
> > +
> > +     memset(p->gpio, 1, sizeof(u8) * p->max_pins);
> > +     for (i = 0; i < p->func_count; i++) {
> > +             if (!p->func[i]->pin_count)
> > +                     continue;
> > +
> > +             for (j = 0; j < p->func[i]->pin_count; j++)
> > +                     p->gpio[p->func[i]->pins[j]] = 0;
> > +     }
> > +
> > +     /* pin 0 is always a gpio */
> > +     p->gpio[0] = 1;
> > +
> > +     /* set the pads */
> > +     for (i = 0; i < p->max_pins; i++) {
> > +             /* strlen("ioXY") + 1 = 5 */
> > +             char *name = devm_kzalloc(p->dev, 5, GFP_KERNEL);
> > +
>
>                 char *name;
>
>                 name = kasprintf(GFP_KERNEL, "io%d", i);
>
>
> > +             if (!name)
> > +                     return -ENOMEM;
> > +             snprintf(name, 5, "io%d", i);
> > +             p->pads[i].number = i;
> > +             p->pads[i].name = name;
> > +     }
> > +     p->desc->pins = p->pads;
> > +     p->desc->npins = p->max_pins;
> > +
> > +     return 0;
> > +}
> > +
> > +static int rt2880_pinmux_probe(struct platform_device *pdev)
> > +{
> > +     struct rt2880_priv *p;
> > +     struct pinctrl_dev *dev;
> > +
> > +     if (!rt2880_pinmux_data)
> > +             return -ENOTSUPP;
> > +
> > +     /* setup the private data */
> > +     p = devm_kzalloc(&pdev->dev, sizeof(struct rt2880_priv), GFP_KERNEL);
> > +     if (!p)
> > +             return -ENOMEM;
> > +
> > +     p->dev = &pdev->dev;
> > +     p->desc = &rt2880_pctrl_desc;
> > +     p->groups = rt2880_pinmux_data;
> > +     platform_set_drvdata(pdev, p);
> > +
> > +     /* init the device */
> > +     if (rt2880_pinmux_index(p)) {
> > +             dev_err(&pdev->dev, "failed to load index\n");
> > +             return -EINVAL;
>
> Preserve the error code:
>
>         err = rt2880_pinmux_index(p);
>         if (err) {
>                 dev_err(&pdev->dev, "failed to load index\n");
>                 return err;
>         }
>
>
> > +     }
> > +     if (rt2880_pinmux_pins(p)) {
> > +             dev_err(&pdev->dev, "failed to load pins\n");
> > +             return -EINVAL;
>
> Same.
>
> > +     }
> > +     dev = pinctrl_register(p->desc, &pdev->dev, p);
> > +     if (IS_ERR(dev))
> > +             return PTR_ERR(dev);
> > +
> > +     return 0;
> > +}

This is already applied but needs more work in its correct place
afterwards. So I will take into account all of these comments and will
send patches to properly address all of them.

>
> regards,
> dan carpenter

Thanks again.

Best regards,
   Sergio Paracuellos
diff mbox series

Patch

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 815095326e2d..e97970ec1210 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -215,6 +215,12 @@  config PINCTRL_ROCKCHIP
 	select MFD_SYSCON
 	select OF_GPIO
 
+config PINCTRL_RT2880
+        bool "RT2880 pinctrl driver for RALINK/Mediatek SOCs"
+        depends on RALINK
+        select PINMUX
+        select GENERIC_PINCONF
+
 config PINCTRL_SINGLE
 	tristate "One-register-per-pin type device tree based pinctrl driver"
 	depends on OF
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f53933b2ff02..0ebc24dad259 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -45,6 +45,7 @@  obj-$(CONFIG_PINCTRL_STMFX) 	+= pinctrl-stmfx.o
 obj-$(CONFIG_PINCTRL_ZYNQ)	+= pinctrl-zynq.o
 obj-$(CONFIG_PINCTRL_INGENIC)	+= pinctrl-ingenic.o
 obj-$(CONFIG_PINCTRL_RK805)	+= pinctrl-rk805.o
+obj-$(CONFIG_PINCTRL_RT2880)   += pinctrl-rt2880.o
 obj-$(CONFIG_PINCTRL_OCELOT)	+= pinctrl-ocelot.o
 obj-$(CONFIG_PINCTRL_EQUILIBRIUM)   += pinctrl-equilibrium.o
 
diff --git a/drivers/pinctrl/pinctrl-rt2880.c b/drivers/pinctrl/pinctrl-rt2880.c
new file mode 100644
index 000000000000..e61dbe186bc9
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-rt2880.c
@@ -0,0 +1,370 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+#include <asm/mach-ralink/pinmux.h>
+#include <asm/mach-ralink/mt7620.h>
+
+#include "core.h"
+#include "pinctrl-utils.h"
+
+#define SYSC_REG_GPIO_MODE	0x60
+#define SYSC_REG_GPIO_MODE2	0x64
+
+struct rt2880_priv {
+	struct device *dev;
+
+	struct pinctrl_pin_desc *pads;
+	struct pinctrl_desc *desc;
+
+	struct rt2880_pmx_func **func;
+	int func_count;
+
+	struct rt2880_pmx_group *groups;
+	const char **group_names;
+	int group_count;
+
+	u8 *gpio;
+	int max_pins;
+};
+
+static int rt2880_get_group_count(struct pinctrl_dev *pctrldev)
+{
+	struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+	return p->group_count;
+}
+
+static const char *rt2880_get_group_name(struct pinctrl_dev *pctrldev,
+					 unsigned int group)
+{
+	struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+	return (group >= p->group_count) ? NULL : p->group_names[group];
+}
+
+static int rt2880_get_group_pins(struct pinctrl_dev *pctrldev,
+				 unsigned int group,
+				 const unsigned int **pins,
+				 unsigned int *num_pins)
+{
+	struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+	if (group >= p->group_count)
+		return -EINVAL;
+
+	*pins = p->groups[group].func[0].pins;
+	*num_pins = p->groups[group].func[0].pin_count;
+
+	return 0;
+}
+
+static const struct pinctrl_ops rt2880_pctrl_ops = {
+	.get_groups_count	= rt2880_get_group_count,
+	.get_group_name		= rt2880_get_group_name,
+	.get_group_pins		= rt2880_get_group_pins,
+	.dt_node_to_map		= pinconf_generic_dt_node_to_map_all,
+	.dt_free_map		= pinconf_generic_dt_free_map,
+};
+
+static int rt2880_pmx_func_count(struct pinctrl_dev *pctrldev)
+{
+	struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+	return p->func_count;
+}
+
+static const char *rt2880_pmx_func_name(struct pinctrl_dev *pctrldev,
+					unsigned int func)
+{
+	struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+	return p->func[func]->name;
+}
+
+static int rt2880_pmx_group_get_groups(struct pinctrl_dev *pctrldev,
+				       unsigned int func,
+				       const char * const **groups,
+				       unsigned int * const num_groups)
+{
+	struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+	if (p->func[func]->group_count == 1)
+		*groups = &p->group_names[p->func[func]->groups[0]];
+	else
+		*groups = p->group_names;
+
+	*num_groups = p->func[func]->group_count;
+
+	return 0;
+}
+
+static int rt2880_pmx_group_enable(struct pinctrl_dev *pctrldev,
+				   unsigned int func, unsigned int group)
+{
+	struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+	u32 mode = 0;
+	u32 reg = SYSC_REG_GPIO_MODE;
+	int i;
+	int shift;
+
+	/* dont allow double use */
+	if (p->groups[group].enabled) {
+		dev_err(p->dev, "%s is already enabled\n",
+			p->groups[group].name);
+		return -EBUSY;
+	}
+
+	p->groups[group].enabled = 1;
+	p->func[func]->enabled = 1;
+
+	shift = p->groups[group].shift;
+	if (shift >= 32) {
+		shift -= 32;
+		reg = SYSC_REG_GPIO_MODE2;
+	}
+	mode = rt_sysc_r32(reg);
+	mode &= ~(p->groups[group].mask << shift);
+
+	/* mark the pins as gpio */
+	for (i = 0; i < p->groups[group].func[0].pin_count; i++)
+		p->gpio[p->groups[group].func[0].pins[i]] = 1;
+
+	/* function 0 is gpio and needs special handling */
+	if (func == 0) {
+		mode |= p->groups[group].gpio << shift;
+	} else {
+		for (i = 0; i < p->func[func]->pin_count; i++)
+			p->gpio[p->func[func]->pins[i]] = 0;
+		mode |= p->func[func]->value << shift;
+	}
+	rt_sysc_w32(mode, reg);
+
+	return 0;
+}
+
+static int rt2880_pmx_group_gpio_request_enable(struct pinctrl_dev *pctrldev,
+						struct pinctrl_gpio_range *range,
+						unsigned int pin)
+{
+	struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+	if (!p->gpio[pin]) {
+		dev_err(p->dev, "pin %d is not set to gpio mux\n", pin);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct pinmux_ops rt2880_pmx_group_ops = {
+	.get_functions_count	= rt2880_pmx_func_count,
+	.get_function_name	= rt2880_pmx_func_name,
+	.get_function_groups	= rt2880_pmx_group_get_groups,
+	.set_mux		= rt2880_pmx_group_enable,
+	.gpio_request_enable	= rt2880_pmx_group_gpio_request_enable,
+};
+
+static struct pinctrl_desc rt2880_pctrl_desc = {
+	.owner		= THIS_MODULE,
+	.name		= "rt2880-pinmux",
+	.pctlops	= &rt2880_pctrl_ops,
+	.pmxops		= &rt2880_pmx_group_ops,
+};
+
+static struct rt2880_pmx_func gpio_func = {
+	.name = "gpio",
+};
+
+static int rt2880_pinmux_index(struct rt2880_priv *p)
+{
+	struct rt2880_pmx_func **f;
+	struct rt2880_pmx_group *mux = p->groups;
+	int i, j, c = 0;
+
+	/* count the mux functions */
+	while (mux->name) {
+		p->group_count++;
+		mux++;
+	}
+
+	/* allocate the group names array needed by the gpio function */
+	p->group_names = devm_kcalloc(p->dev, p->group_count,
+				      sizeof(char *), GFP_KERNEL);
+	if (!p->group_names)
+		return -1;
+
+	for (i = 0; i < p->group_count; i++) {
+		p->group_names[i] = p->groups[i].name;
+		p->func_count += p->groups[i].func_count;
+	}
+
+	/* we have a dummy function[0] for gpio */
+	p->func_count++;
+
+	/* allocate our function and group mapping index buffers */
+	f = p->func = devm_kcalloc(p->dev,
+				   p->func_count,
+				   sizeof(*p->func),
+				   GFP_KERNEL);
+	gpio_func.groups = devm_kcalloc(p->dev, p->group_count, sizeof(int),
+					GFP_KERNEL);
+	if (!f || !gpio_func.groups)
+		return -1;
+
+	/* add a backpointer to the function so it knows its group */
+	gpio_func.group_count = p->group_count;
+	for (i = 0; i < gpio_func.group_count; i++)
+		gpio_func.groups[i] = i;
+
+	f[c] = &gpio_func;
+	c++;
+
+	/* add remaining functions */
+	for (i = 0; i < p->group_count; i++) {
+		for (j = 0; j < p->groups[i].func_count; j++) {
+			f[c] = &p->groups[i].func[j];
+			f[c]->groups = devm_kzalloc(p->dev, sizeof(int),
+						    GFP_KERNEL);
+			f[c]->groups[0] = i;
+			f[c]->group_count = 1;
+			c++;
+		}
+	}
+	return 0;
+}
+
+static int rt2880_pinmux_pins(struct rt2880_priv *p)
+{
+	int i, j;
+
+	/*
+	 * loop over the functions and initialize the pins array.
+	 * also work out the highest pin used.
+	 */
+	for (i = 0; i < p->func_count; i++) {
+		int pin;
+
+		if (!p->func[i]->pin_count)
+			continue;
+
+		p->func[i]->pins = devm_kcalloc(p->dev,
+						p->func[i]->pin_count,
+						sizeof(int),
+						GFP_KERNEL);
+		for (j = 0; j < p->func[i]->pin_count; j++)
+			p->func[i]->pins[j] = p->func[i]->pin_first + j;
+
+		pin = p->func[i]->pin_first + p->func[i]->pin_count;
+		if (pin > p->max_pins)
+			p->max_pins = pin;
+	}
+
+	/* the buffer that tells us which pins are gpio */
+	p->gpio = devm_kcalloc(p->dev, p->max_pins, sizeof(u8), GFP_KERNEL);
+	/* the pads needed to tell pinctrl about our pins */
+	p->pads = devm_kcalloc(p->dev, p->max_pins,
+			       sizeof(struct pinctrl_pin_desc), GFP_KERNEL);
+	if (!p->pads || !p->gpio) {
+		dev_err(p->dev, "Failed to allocate gpio data\n");
+		return -ENOMEM;
+	}
+
+	memset(p->gpio, 1, sizeof(u8) * p->max_pins);
+	for (i = 0; i < p->func_count; i++) {
+		if (!p->func[i]->pin_count)
+			continue;
+
+		for (j = 0; j < p->func[i]->pin_count; j++)
+			p->gpio[p->func[i]->pins[j]] = 0;
+	}
+
+	/* pin 0 is always a gpio */
+	p->gpio[0] = 1;
+
+	/* set the pads */
+	for (i = 0; i < p->max_pins; i++) {
+		/* strlen("ioXY") + 1 = 5 */
+		char *name = devm_kzalloc(p->dev, 5, GFP_KERNEL);
+
+		if (!name)
+			return -ENOMEM;
+		snprintf(name, 5, "io%d", i);
+		p->pads[i].number = i;
+		p->pads[i].name = name;
+	}
+	p->desc->pins = p->pads;
+	p->desc->npins = p->max_pins;
+
+	return 0;
+}
+
+static int rt2880_pinmux_probe(struct platform_device *pdev)
+{
+	struct rt2880_priv *p;
+	struct pinctrl_dev *dev;
+
+	if (!rt2880_pinmux_data)
+		return -ENOTSUPP;
+
+	/* setup the private data */
+	p = devm_kzalloc(&pdev->dev, sizeof(struct rt2880_priv), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+
+	p->dev = &pdev->dev;
+	p->desc = &rt2880_pctrl_desc;
+	p->groups = rt2880_pinmux_data;
+	platform_set_drvdata(pdev, p);
+
+	/* init the device */
+	if (rt2880_pinmux_index(p)) {
+		dev_err(&pdev->dev, "failed to load index\n");
+		return -EINVAL;
+	}
+	if (rt2880_pinmux_pins(p)) {
+		dev_err(&pdev->dev, "failed to load pins\n");
+		return -EINVAL;
+	}
+	dev = pinctrl_register(p->desc, &pdev->dev, p);
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+
+	return 0;
+}
+
+static const struct of_device_id rt2880_pinmux_match[] = {
+	{ .compatible = "ralink,rt2880-pinmux" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rt2880_pinmux_match);
+
+static struct platform_driver rt2880_pinmux_driver = {
+	.probe = rt2880_pinmux_probe,
+	.driver = {
+		.name = "rt2880-pinmux",
+		.of_match_table = rt2880_pinmux_match,
+	},
+};
+
+int __init rt2880_pinmux_init(void)
+{
+	return platform_driver_register(&rt2880_pinmux_driver);
+}
+
+core_initcall_sync(rt2880_pinmux_init);