Message ID | 1420846493-31647-4-git-send-email-andrew@lunn.ch |
---|---|
State | New, archived |
Headers | show |
On Sat, 10 Jan 2015 00:34:49 +0100, Andrew Lunn <andrew@lunn.ch> wrote: <snip> > diff --git a/drivers/gpio/gpio-mvebu-pwm.c > b/drivers/gpio/gpio-mvebu-pwm.c > new file mode 100644 > index 000000000000..8a5af11aed67 > --- /dev/null > +++ b/drivers/gpio/gpio-mvebu-pwm.c > @@ -0,0 +1,202 @@ > +#include <linux/err.h> > +#include <linux/module.h> > +#include <linux/gpio.h> > +#include <linux/pwm.h> > +#include <linux/clk.h> > +#include <linux/platform_device.h> > +#include "gpio-mvebu.h" > +#include "gpiolib.h" You also need to include <asm/io.h> for readl_relaxed, and a short description/copyright on the top wouldn't hurt. Imre -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Jan 12, 2015 at 12:05:50PM +0100, Imre Kaloz wrote: > On Sat, 10 Jan 2015 00:34:49 +0100, Andrew Lunn <andrew@lunn.ch> wrote: > > <snip> > > >diff --git a/drivers/gpio/gpio-mvebu-pwm.c > >b/drivers/gpio/gpio-mvebu-pwm.c > >new file mode 100644 > >index 000000000000..8a5af11aed67 > >--- /dev/null > >+++ b/drivers/gpio/gpio-mvebu-pwm.c > >@@ -0,0 +1,202 @@ > >+#include <linux/err.h> > >+#include <linux/module.h> > >+#include <linux/gpio.h> > >+#include <linux/pwm.h> > >+#include <linux/clk.h> > >+#include <linux/platform_device.h> > >+#include "gpio-mvebu.h" > >+#include "gpiolib.h" > > You also need to include <asm/io.h> for readl_relaxed, and a short > description/copyright on the top wouldn't hurt. Will do. Thanks Andrew -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Sat, Jan 10, 2015 at 12:34 AM, Andrew Lunn <andrew@lunn.ch> wrote: > Armada 370/XP devices can 'blink' gpio lines with a configurable on > and off period. This can be modelled as a PWM. > > However, there are only two sets of PWM configuration registers for > all the gpio lines. This driver simply allows a single gpio line per > gpio chip of 32 lines to be used as a PWM. Attempts to use more return > EBUSY. > > Due to the interleaving of registers it is not simple to separate the > PWM driver from the gpio driver. Thus the gpio driver has been > extended with a PWM driver. > > Signed-off-by: Andrew Lunn <andrew@lunn.ch> > --- > drivers/gpio/Kconfig | 5 ++ > drivers/gpio/Makefile | 1 + > drivers/gpio/gpio-mvebu-pwm.c | 202 ++++++++++++++++++++++++++++++++++++++++++ (...) > +++ b/drivers/gpio/gpio-mvebu-pwm.c (...) > +static const struct pwm_ops mvebu_pwm_ops = { > + .request = mvebu_pwm_request, > + .free = mvebu_pwm_free, > + .config = mvebu_pwm_config, > + .enable = mvebu_pwm_enable, > + .disable = mvebu_pwm_disable, > + .owner = THIS_MODULE, > +}; So the first basic notes: - PWM maintainer Thierry Reding has to review and ACK this, and the patch needs to be posted to the linux-pwm mailing list too (check To: line) - Should that driver portion really be in drivers/gpio or rather in drivers/pwm - Why is this not modeled as an MFD spawning a GPIO and a PWM cell, as is custom? (Bringing MFD maintainers into the picture.) So I am aware that this takes the problem from "quick fix extension to the GPIO driver" to "really nasty hairy re-engineering of the whole shebang" but there is a lot of precedents in the kernel for splitting up this type of hardware in separate drivers under an MFD hub. Yours, Linus Walleij -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Jan 12, 2015 at 03:06:14PM +0100, Linus Walleij wrote: > On Sat, Jan 10, 2015 at 12:34 AM, Andrew Lunn <andrew@lunn.ch> wrote: > > > Armada 370/XP devices can 'blink' gpio lines with a configurable on > > and off period. This can be modelled as a PWM. > > > > However, there are only two sets of PWM configuration registers for > > all the gpio lines. This driver simply allows a single gpio line per > > gpio chip of 32 lines to be used as a PWM. Attempts to use more return > > EBUSY. > > > > Due to the interleaving of registers it is not simple to separate the > > PWM driver from the gpio driver. Thus the gpio driver has been > > extended with a PWM driver. > > > > Signed-off-by: Andrew Lunn <andrew@lunn.ch> > > --- > > drivers/gpio/Kconfig | 5 ++ > > drivers/gpio/Makefile | 1 + > > drivers/gpio/gpio-mvebu-pwm.c | 202 ++++++++++++++++++++++++++++++++++++++++++ > (...) > > +++ b/drivers/gpio/gpio-mvebu-pwm.c > (...) > > +static const struct pwm_ops mvebu_pwm_ops = { > > + .request = mvebu_pwm_request, > > + .free = mvebu_pwm_free, > > + .config = mvebu_pwm_config, > > + .enable = mvebu_pwm_enable, > > + .disable = mvebu_pwm_disable, > > + .owner = THIS_MODULE, > > +}; > > So the first basic notes: > > - PWM maintainer Thierry Reding has to review and ACK this, and the patch needs > to be posted to the linux-pwm mailing list too (check To: line) Yes, Thierry was in To: and linux-pwm in Cc: > - Should that driver portion really be in drivers/gpio or rather in drivers/pwm I also had the same thoughts. What decided it for me is the use of gpiolib.h. That file would have to move somewhere under include/linux, or i find a different way of doing what i'm doing without using it. > - Why is this not modeled as an MFD spawning a GPIO and a PWM cell, > as is custom? (Bringing MFD maintainers into the picture.) > > So I am aware that this takes the problem from "quick fix extension to the GPIO > driver" to "really nasty hairy re-engineering of the whole shebang" but there is > a lot of precedents in the kernel for splitting up this type of hardware in > separate drivers under an MFD hub. Yes, one example would be lp3943, an i2c GPIO and PWM driver. The nasty hairy re-engineering is one thing that is putting me off. Another is keeping DT compatibility. Here i'm just adding some additional optional properties. I've not looked in detail, but i think it is going to be really hard to add an MFD driver without breaking the existing DT binding. That is not somewhere i want to go, i'd just drop this altogether. Do you know of any other driver with a DT binding which has been refactored into an MFD? An example to look at would be useful. Thanks Andrew -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
> - Why is this not modeled as an MFD spawning a GPIO and a PWM cell, > as is custom? (Bringing MFD maintainers into the picture.) > > So I am aware that this takes the problem from "quick fix extension to the GPIO > driver" to "really nasty hairy re-engineering of the whole shebang" but there is > a lot of precedents in the kernel for splitting up this type of hardware in > separate drivers under an MFD hub. Hi Linus So i thought about this some more. What would an MFD based solution look like? First issue is backwards compatibility. There are currently around 90 .dts files using this gpio driver. I could imagine a few of these being changed to make use of an MFD based driver to make us of the new features, but the rest expect backwards compatibility. I think the only sensible way to achieve this is that the gpio driver keeps its existing binding. We then need to add a new MFD binding, with sub sections for the gpio driver and the PWM driver. At a first stab, it would look something like: armada-gpio { compatible = "marvell,armada-gpio"; reg = <0xd0018100 0x40>, <0xd00181c0 0x08>; gpio: gpio { compatible = "marvell,armada-gpio-gpio"; ngpios = <32>; gio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; interrupts = <16>, <17>, <18>, <19>; clocks = <&coreclk 0>; }; pwm: pwm { compatible = "marvell,armada-gpio-pwm"; #pwm-cells = <2>; clocks = <&coreclk 0>; }; }; This does not really describe the hardware. The hardware is more like: gpio: gpio { compatible = "marvell,orion-gpio"; reg = <0xd0018100 0x40>; ngpios = <32>; gio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; interrupts = <16>, <17>, <18>, <19>; clocks = <&coreclk 0>; pwm: pwm { compatible = "marvell,armada-pwm"; reg = <0xd00181c0 0x08>; #pwm-cells = <2>; clocks = <&coreclk 0>; }; }; but i don't think MFD supports that sort of structure? So assuming we go with the first binding above, this means the gpio driver gains a second binding, probe function, etc for when it is probed as part of an MFD. Now it starts getting nasty hairy. The MFD core driver should offer a set of functions to access the hardware, which are shared by the gpio-gpio driver and the gpio-pwm driver. However when the gpio driver is probed using its current binding, not the MFD binding, it also needs access functions. So we need a library of access functions, which can be used directly or via the MFD code. Where do we place these? In the gpio driver would make sense, since gpio driver always needs them, the MFD is optional. But they could be in the MFD driver. The gpio driver is currently built in, meaning the MFD driver would be built in, so even if the MFD driver is never probed, it can still export functions. Memory management then becomes fun. If the MFD is probed, it needs to allocated the shared memory, used by the access functions etc. If the standalone gpio driver is probed, it needs to allocated the memory used by the access functions etc. I'm also worried about race conditions during probe. The pwm driver is not independent of the gpio driver. It is a child of the gpio driver. It needs to know the gpiochip base and ngpio during its own probe function. In effect, we need to ensure that the MFD gpio probe has completed successfully before the MFD pwm probe is called. Nothing completely unsolvable, it just seems ugly and complex. Humm, that second binding just gave me an idea. The pwm driver is a child of the gpio driver. That is the same relationship between an MFD driver and its children. So maybe we should move mvebu-gpio.c into drives/mfd and add some mfd functionality so it can mfd_add_device() the pwm driver when it has completed its own probe? That nicely solves the probe race issue, and the library of access functions etc. The gpio binding is also backwards compatible since it is being extended with an optional MFD child device. Andrew -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Jan 13, 2015 at 3:42 AM, Andrew Lunn <andrew@lunn.ch> wrote: > So i thought about this some more. What would an MFD based solution > look like? > > First issue is backwards compatibility. There are currently around 90 > .dts files using this gpio driver. I could imagine a few of these > being changed to make use of an MFD based driver to make us of the new > features, but the rest expect backwards compatibility. Good point. > I think the only sensible way to achieve this is that the gpio driver > keeps its existing binding. Yup. > This does not really describe the hardware. The hardware is more like: > > gpio: gpio { > compatible = "marvell,orion-gpio"; > reg = <0xd0018100 0x40>; > ngpios = <32>; > gio-controller; > #gpio-cells = <2>; > interrupt-controller; > #interrupt-cells = <2>; > interrupts = <16>, <17>, <18>, <19>; > clocks = <&coreclk 0>; > > pwm: pwm { > compatible = "marvell,armada-pwm"; > reg = <0xd00181c0 0x08>; > #pwm-cells = <2>; > clocks = <&coreclk 0>; > }; > }; > > but i don't think MFD supports that sort of structure? No it would have to be some custom DT code in the GPIO driver spawning the PWM platform device. I think it's better if we either go with the first solution of a combined GPIO+PWM node (it's also elegant in a way, and perfectly OK with device tree I think) but I want the PWM maintainer to say if it's OK to have a PWM driver inside a GPIO driver. Yours, Linus Walleij -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
> I think it's better if we either go with the first solution of a combined > GPIO+PWM node (it's also elegant in a way, and perfectly > OK with device tree I think) but I want the PWM maintainer to > say if it's OK to have a PWM driver inside a GPIO driver. Hi Thierry Please could you comment on this. I would like to get moving forward with getting these patches accepted. Thanks Andrew -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, 15 Jan 2015, Linus Walleij wrote: > On Tue, Jan 13, 2015 at 3:42 AM, Andrew Lunn <andrew@lunn.ch> wrote: > > > So i thought about this some more. What would an MFD based solution > > look like? > > > > First issue is backwards compatibility. There are currently around 90 > > .dts files using this gpio driver. I could imagine a few of these > > being changed to make use of an MFD based driver to make us of the new > > features, but the rest expect backwards compatibility. > > Good point. > > > I think the only sensible way to achieve this is that the gpio driver > > keeps its existing binding. > > Yup. > > > This does not really describe the hardware. The hardware is more like: > > > > gpio: gpio { > > compatible = "marvell,orion-gpio"; > > reg = <0xd0018100 0x40>; > > ngpios = <32>; > > gio-controller; > > #gpio-cells = <2>; > > interrupt-controller; > > #interrupt-cells = <2>; > > interrupts = <16>, <17>, <18>, <19>; > > clocks = <&coreclk 0>; > > > > pwm: pwm { > > compatible = "marvell,armada-pwm"; > > reg = <0xd00181c0 0x08>; > > #pwm-cells = <2>; > > clocks = <&coreclk 0>; > > }; > > }; > > > > but i don't think MFD supports that sort of structure? > > No it would have to be some custom DT code in the GPIO driver > spawning the PWM platform device. of_platform_populate()?
On Sun, Jan 18, 2015 at 10:04:38AM +0000, Lee Jones wrote: > On Thu, 15 Jan 2015, Linus Walleij wrote: > > > On Tue, Jan 13, 2015 at 3:42 AM, Andrew Lunn <andrew@lunn.ch> wrote: > > > > > So i thought about this some more. What would an MFD based solution > > > look like? > > > > > > First issue is backwards compatibility. There are currently around 90 > > > .dts files using this gpio driver. I could imagine a few of these > > > being changed to make use of an MFD based driver to make us of the new > > > features, but the rest expect backwards compatibility. > > > > Good point. > > > > > I think the only sensible way to achieve this is that the gpio driver > > > keeps its existing binding. > > > > Yup. > > > > > This does not really describe the hardware. The hardware is more like: > > > > > > gpio: gpio { > > > compatible = "marvell,orion-gpio"; > > > reg = <0xd0018100 0x40>; > > > ngpios = <32>; > > > gio-controller; > > > #gpio-cells = <2>; > > > interrupt-controller; > > > #interrupt-cells = <2>; > > > interrupts = <16>, <17>, <18>, <19>; > > > clocks = <&coreclk 0>; > > > > > > pwm: pwm { > > > compatible = "marvell,armada-pwm"; > > > reg = <0xd00181c0 0x08>; > > > #pwm-cells = <2>; > > > clocks = <&coreclk 0>; > > > }; > > > }; I think you technically need an (empty) ranges property in the gpio node so that the address can be properly translated. > > > > > > but i don't think MFD supports that sort of structure? > > > > No it would have to be some custom DT code in the GPIO driver > > spawning the PWM platform device. > > of_platform_populate()? Huh? I was under the impression that mfd_add_devices() would already deal with this situation? Isn't that what's parsed based on the cells parameter? Thierry
On Thu, Jan 15, 2015 at 10:52:51AM +0100, Linus Walleij wrote: > On Tue, Jan 13, 2015 at 3:42 AM, Andrew Lunn <andrew@lunn.ch> wrote: > > > So i thought about this some more. What would an MFD based solution > > look like? > > > > First issue is backwards compatibility. There are currently around 90 > > .dts files using this gpio driver. I could imagine a few of these > > being changed to make use of an MFD based driver to make us of the new > > features, but the rest expect backwards compatibility. > > Good point. > > > I think the only sensible way to achieve this is that the gpio driver > > keeps its existing binding. > > Yup. > > > This does not really describe the hardware. The hardware is more like: > > > > gpio: gpio { > > compatible = "marvell,orion-gpio"; > > reg = <0xd0018100 0x40>; > > ngpios = <32>; > > gio-controller; > > #gpio-cells = <2>; > > interrupt-controller; > > #interrupt-cells = <2>; > > interrupts = <16>, <17>, <18>, <19>; > > clocks = <&coreclk 0>; > > > > pwm: pwm { > > compatible = "marvell,armada-pwm"; > > reg = <0xd00181c0 0x08>; > > #pwm-cells = <2>; > > clocks = <&coreclk 0>; > > }; > > }; > > > > but i don't think MFD supports that sort of structure? > > No it would have to be some custom DT code in the GPIO driver > spawning the PWM platform device. > > I think it's better if we either go with the first solution of a combined > GPIO+PWM node (it's also elegant in a way, and perfectly > OK with device tree I think) but I want the PWM maintainer to > say if it's OK to have a PWM driver inside a GPIO driver. I'm fine with that, too. I'd request an update to MAINTAINERS so that at least linux-pwm@vger.kernel.org gets included on patches against the driver. That said, the above DT description would lend itself nicely to MFD in my opinion. Thierry
On Mon, 19 Jan 2015, Thierry Reding wrote: > On Sun, Jan 18, 2015 at 10:04:38AM +0000, Lee Jones wrote: > > On Thu, 15 Jan 2015, Linus Walleij wrote: > > > > > On Tue, Jan 13, 2015 at 3:42 AM, Andrew Lunn <andrew@lunn.ch> wrote: > > > > > > > So i thought about this some more. What would an MFD based solution > > > > look like? > > > > > > > > First issue is backwards compatibility. There are currently around 90 > > > > .dts files using this gpio driver. I could imagine a few of these > > > > being changed to make use of an MFD based driver to make us of the new > > > > features, but the rest expect backwards compatibility. > > > > > > Good point. > > > > > > > I think the only sensible way to achieve this is that the gpio driver > > > > keeps its existing binding. > > > > > > Yup. > > > > > > > This does not really describe the hardware. The hardware is more like: > > > > > > > > gpio: gpio { > > > > compatible = "marvell,orion-gpio"; > > > > reg = <0xd0018100 0x40>; > > > > ngpios = <32>; > > > > gio-controller; > > > > #gpio-cells = <2>; > > > > interrupt-controller; > > > > #interrupt-cells = <2>; > > > > interrupts = <16>, <17>, <18>, <19>; > > > > clocks = <&coreclk 0>; > > > > > > > > pwm: pwm { > > > > compatible = "marvell,armada-pwm"; > > > > reg = <0xd00181c0 0x08>; > > > > #pwm-cells = <2>; > > > > clocks = <&coreclk 0>; > > > > }; > > > > }; > > I think you technically need an (empty) ranges property in the gpio node > so that the address can be properly translated. > > > > > > > > > but i don't think MFD supports that sort of structure? > > > > > > No it would have to be some custom DT code in the GPIO driver > > > spawning the PWM platform device. > > > > of_platform_populate()? > > Huh? I was under the impression that mfd_add_devices() would already > deal with this situation? Isn't that what's parsed based on the cells > parameter? There was talk of this _not_ being an MFD device. I was providing an alternative way to get this device registered.
On Sat, Jan 10, 2015 at 12:34:49AM +0100, Andrew Lunn wrote: > Armada 370/XP devices can 'blink' gpio lines with a configurable on > and off period. This can be modelled as a PWM. > > However, there are only two sets of PWM configuration registers for > all the gpio lines. This driver simply allows a single gpio line per > gpio chip of 32 lines to be used as a PWM. Attempts to use more return > EBUSY. > > Due to the interleaving of registers it is not simple to separate the > PWM driver from the gpio driver. Thus the gpio driver has been > extended with a PWM driver. > > Signed-off-by: Andrew Lunn <andrew@lunn.ch> > --- > drivers/gpio/Kconfig | 5 ++ > drivers/gpio/Makefile | 1 + > drivers/gpio/gpio-mvebu-pwm.c | 202 ++++++++++++++++++++++++++++++++++++++++++ > drivers/gpio/gpio-mvebu.c | 37 +++----- > drivers/gpio/gpio-mvebu.h | 79 +++++++++++++++++ > 5 files changed, 299 insertions(+), 25 deletions(-) > create mode 100644 drivers/gpio/gpio-mvebu-pwm.c > create mode 100644 drivers/gpio/gpio-mvebu.h FWIW, I think this could easily just be all in the gpio-mvebu.c driver, no need to split it up. Thierry
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 633ec216e185..be98d531d735 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -239,6 +239,11 @@ config GPIO_MVEBU select GPIO_GENERIC select GENERIC_IRQ_CHIP +config GPIO_MVEBU_PWM + def_bool y + depends on GPIO_MVEBU + depends on PWM + config GPIO_MXC def_bool y depends on ARCH_MXC diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 81755f1305e6..be94f47c1ddb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o +obj-$(CONFIG_GPIO_MVEBU_PWM) += gpio-mvebu-pwm.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o diff --git a/drivers/gpio/gpio-mvebu-pwm.c b/drivers/gpio/gpio-mvebu-pwm.c new file mode 100644 index 000000000000..8a5af11aed67 --- /dev/null +++ b/drivers/gpio/gpio-mvebu-pwm.c @@ -0,0 +1,202 @@ +#include <linux/err.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/pwm.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include "gpio-mvebu.h" +#include "gpiolib.h" + +static void __iomem *mvebu_gpioreg_blink_select(struct mvebu_gpio_chip *mvchip) +{ + return mvchip->membase + GPIO_BLINK_CNT_SELECT; +} + +static inline struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip) +{ + return container_of(chip, struct mvebu_pwm, chip); +} + +static inline struct mvebu_gpio_chip *to_mvchip(struct mvebu_pwm *pwm) +{ + return container_of(pwm, struct mvebu_gpio_chip, pwm); +} + +static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwmd) +{ + struct mvebu_pwm *pwm = to_mvebu_pwm(chip); + struct mvebu_gpio_chip *mvchip = to_mvchip(pwm); + struct gpio_desc *desc = gpio_to_desc(pwmd->pwm); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&pwm->lock, flags); + if (pwm->used) { + ret = -EBUSY; + } else { + if (!desc) { + ret = -ENODEV; + goto out; + } + ret = gpiod_request(desc, "mvebu-pwm"); + if (ret) + goto out; + + ret = gpiod_direction_output(desc, 0); + if (ret) { + gpiod_free(desc); + goto out; + } + + pwm->pin = pwmd->pwm - mvchip->chip.base; + pwm->used = true; + } + +out: + spin_unlock_irqrestore(&pwm->lock, flags); + return ret; +} + +static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwmd) +{ + struct mvebu_pwm *pwm = to_mvebu_pwm(chip); + struct gpio_desc *desc = gpio_to_desc(pwmd->pwm); + unsigned long flags; + + spin_lock_irqsave(&pwm->lock, flags); + gpiod_free(desc); + pwm->used = false; + spin_unlock_irqrestore(&pwm->lock, flags); +} + +static int mvebu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwmd, + int duty_ns, int period_ns) +{ + struct mvebu_pwm *pwm = to_mvebu_pwm(chip); + struct mvebu_gpio_chip *mvchip = to_mvchip(pwm); + unsigned int on, off; + unsigned long long val; + u32 u; + + val = (unsigned long long) pwm->clk_rate * duty_ns; + do_div(val, NSEC_PER_SEC); + if (val > UINT_MAX) + return -EINVAL; + if (val) + on = val; + else + on = 1; + + val = (unsigned long long) pwm->clk_rate * (period_ns - duty_ns); + do_div(val, NSEC_PER_SEC); + if (val > UINT_MAX) + return -EINVAL; + if (val) + off = val; + else + off = 1; + + u = readl_relaxed(mvebu_gpioreg_blink_select(mvchip)); + u &= ~(1 << pwm->pin); + u |= (pwm->id << pwm->pin); + writel_relaxed(u, mvebu_gpioreg_blink_select(mvchip)); + + writel_relaxed(on, pwm->membase + BLINK_ON_DURATION); + writel_relaxed(off, pwm->membase + BLINK_OFF_DURATION); + + return 0; +} + +static int mvebu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwmd) +{ + struct mvebu_pwm *pwm = to_mvebu_pwm(chip); + struct mvebu_gpio_chip *mvchip = to_mvchip(pwm); + + mvebu_gpio_blink(&mvchip->chip, pwm->pin, 1); + + return 0; +} + +static void mvebu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwmd) +{ + struct mvebu_pwm *pwm = to_mvebu_pwm(chip); + struct mvebu_gpio_chip *mvchip = to_mvchip(pwm); + + mvebu_gpio_blink(&mvchip->chip, pwm->pin, 0); +} + +static const struct pwm_ops mvebu_pwm_ops = { + .request = mvebu_pwm_request, + .free = mvebu_pwm_free, + .config = mvebu_pwm_config, + .enable = mvebu_pwm_enable, + .disable = mvebu_pwm_disable, + .owner = THIS_MODULE, +}; + +void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) +{ + struct mvebu_pwm *pwm = &mvchip->pwm; + + pwm->blink_select = readl_relaxed(mvebu_gpioreg_blink_select(mvchip)); + pwm->blink_on_duration = + readl_relaxed(pwm->membase + BLINK_ON_DURATION); + pwm->blink_off_duration = + readl_relaxed(pwm->membase + BLINK_OFF_DURATION); +} + +void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) +{ + struct mvebu_pwm *pwm = &mvchip->pwm; + + writel_relaxed(pwm->blink_select, mvebu_gpioreg_blink_select(mvchip)); + writel_relaxed(pwm->blink_on_duration, + pwm->membase + BLINK_ON_DURATION); + writel_relaxed(pwm->blink_off_duration, + pwm->membase + BLINK_OFF_DURATION); +} + +/* + * Armada 370/XP has simple PWM support for gpio lines. Other SoCs + * don't have this hardware. So if we don't have the necessary + * resource, it is not an error. + */ +int mvebu_pwm_probe(struct platform_device *pdev, + struct mvebu_gpio_chip *mvchip, + int id) +{ + struct device *dev = &pdev->dev; + struct mvebu_pwm *pwm = &mvchip->pwm; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"); + if (!res) + return 0; + + mvchip->pwm.membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mvchip->pwm.membase)) + return PTR_ERR(mvchip->percpu_membase); + + if (id < 0 || id > 1) + return -EINVAL; + pwm->id = id; + + if (IS_ERR(mvchip->clk)) + return PTR_ERR(mvchip->clk); + + pwm->clk_rate = clk_get_rate(mvchip->clk); + if (!pwm->clk_rate) { + dev_err(dev, "failed to get clock rate\n"); + return -EINVAL; + } + + pwm->chip.dev = dev; + pwm->chip.ops = &mvebu_pwm_ops; + pwm->chip.base = mvchip->chip.base; + pwm->chip.npwm = mvchip->chip.ngpio; + pwm->chip.can_sleep = false; + + spin_lock_init(&pwm->lock); + + return pwmchip_add(&pwm->chip); +} diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index d0bc123c7975..147e76cb57e4 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -42,10 +42,11 @@ #include <linux/io.h> #include <linux/of_irq.h> #include <linux/of_device.h> +#include <linux/pwm.h> #include <linux/clk.h> #include <linux/pinctrl/consumer.h> #include <linux/irqchip/chained_irq.h> - +#include "gpio-mvebu.h" /* * GPIO unit register offsets. */ @@ -75,24 +76,6 @@ #define MVEBU_MAX_GPIO_PER_BANK 32 -struct mvebu_gpio_chip { - struct gpio_chip chip; - spinlock_t lock; - void __iomem *membase; - void __iomem *percpu_membase; - int irqbase; - struct irq_domain *domain; - int soc_variant; - - /* Used to preserve GPIO registers across suspend/resume */ - u32 out_reg; - u32 io_conf_reg; - u32 blink_en_reg; - u32 in_pol_reg; - u32 edge_mask_regs[4]; - u32 level_mask_regs[4]; -}; - /* * Functions returning addresses of individual registers for a given * GPIO controller. @@ -228,7 +211,7 @@ static int mvebu_gpio_get(struct gpio_chip *chip, unsigned pin) return (u >> pin) & 1; } -static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value) +void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value) { struct mvebu_gpio_chip *mvchip = container_of(chip, struct mvebu_gpio_chip, chip); @@ -609,6 +592,8 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) BUG(); } + mvebu_pwm_suspend(mvchip); + return 0; } @@ -652,6 +637,8 @@ static int mvebu_gpio_resume(struct platform_device *pdev) BUG(); } + mvebu_pwm_resume(mvchip); + return 0; } @@ -663,7 +650,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) struct resource *res; struct irq_chip_generic *gc; struct irq_chip_type *ct; - struct clk *clk; unsigned int ngpios; int soc_variant; int i, cpu, id; @@ -693,10 +679,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev) return id; } - clk = devm_clk_get(&pdev->dev, NULL); + mvchip->clk = devm_clk_get(&pdev->dev, NULL); /* Not all SoCs require a clock.*/ - if (!IS_ERR(clk)) - clk_prepare_enable(clk); + if (!IS_ERR(mvchip->clk)) + clk_prepare_enable(mvchip->clk); mvchip->soc_variant = soc_variant; mvchip->chip.label = dev_name(&pdev->dev); @@ -830,7 +816,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev) goto err_generic_chip; } - return 0; + /* Armada 370/XP has simple PWM support for gpio lines */ + return mvebu_pwm_probe(pdev, mvchip, id); err_generic_chip: irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST, diff --git a/drivers/gpio/gpio-mvebu.h b/drivers/gpio/gpio-mvebu.h new file mode 100644 index 000000000000..7cd8c80c06da --- /dev/null +++ b/drivers/gpio/gpio-mvebu.h @@ -0,0 +1,79 @@ +/* + * Interface between MVEBU GPIO driver and PWM driver for GPIO pins + * + * Copyright (C) 2015, Andrew Lunn <andrew@lunn.ch> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MVEBU_GPIO_PWM_H +#define MVEBU_GPIO_PWM_H + +#define BLINK_ON_DURATION 0x0 +#define BLINK_OFF_DURATION 0x4 +#define GPIO_BLINK_CNT_SELECT 0x0020 + +struct mvebu_pwm { + void __iomem *membase; + unsigned long clk_rate; + bool used; + unsigned pin; + struct pwm_chip chip; + int id; + spinlock_t lock; + + /* Used to preserve GPIO/PWM registers across suspend / + * resume */ + u32 blink_select; + u32 blink_on_duration; + u32 blink_off_duration; +}; + +struct mvebu_gpio_chip { + struct gpio_chip chip; + spinlock_t lock; + void __iomem *membase; + void __iomem *percpu_membase; + int irqbase; + struct irq_domain *domain; + int soc_variant; + struct clk *clk; +#ifdef CONFIG_PWM + struct mvebu_pwm pwm; +#endif + /* Used to preserve GPIO registers across suspend/resume */ + u32 out_reg; + u32 io_conf_reg; + u32 blink_en_reg; + u32 in_pol_reg; + u32 edge_mask_regs[4]; + u32 level_mask_regs[4]; +}; + +void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value); + +#ifdef CONFIG_PWM +int mvebu_pwm_probe(struct platform_device *pdev, + struct mvebu_gpio_chip *mvchip, + int id); +void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip); +void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip); +#else +int mvebu_pwm_probe(struct platform_device *pdev, + struct mvebu_gpio_chip *mvchip, + int id) +{ + return 0; +} + +void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) +{ +} + +void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) +{ +} +#endif +#endif
Armada 370/XP devices can 'blink' gpio lines with a configurable on and off period. This can be modelled as a PWM. However, there are only two sets of PWM configuration registers for all the gpio lines. This driver simply allows a single gpio line per gpio chip of 32 lines to be used as a PWM. Attempts to use more return EBUSY. Due to the interleaving of registers it is not simple to separate the PWM driver from the gpio driver. Thus the gpio driver has been extended with a PWM driver. Signed-off-by: Andrew Lunn <andrew@lunn.ch> --- drivers/gpio/Kconfig | 5 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-mvebu-pwm.c | 202 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpio-mvebu.c | 37 +++----- drivers/gpio/gpio-mvebu.h | 79 +++++++++++++++++ 5 files changed, 299 insertions(+), 25 deletions(-) create mode 100644 drivers/gpio/gpio-mvebu-pwm.c create mode 100644 drivers/gpio/gpio-mvebu.h