All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 00/17] Add PWM framework and device tree support
@ 2012-04-10 15:06 ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

This patch series adds very rudimentary device-tree support for PWM
devices. With all of these patches applied (plus one board-specific
patch that is not included), I'm able to control the backlight on the
device I'm working on using the sysfs interface provided by the pwm-bl
driver and the backlight class.

This series is based on Sascha Hauer's series of patches[0] to add a
generic PWM framework. The first patch in this series is taken from
Sascha's branch, while the second patch enables each PWM chip to provide
multiple PWM devices (the Blackfin and PXA drivers have been ported to
the framework for reference). Currently a global namespace is still
provided to keep backwards-compatibility with the legacy PWM API. In
order to achieve this, the number of global PWM devices is limited to
1024. However, patch 2 introduces per-chip indexing of PWM devices in
the core and patch 4 adds an API to request a PWM device using a lookup
table registered by board setup code. They are supposed to eventually
replace pwm_request() and allow to get rid of the global namespace. The
device tree support code does not use the global namespace.

Patch 3 adds a debugfs interface.

Patch 5 adds some code to lookup a PWM chip given its device-tree
handle. This code will be used later on by the pwm-bl driver to find the
PWM device that it should be using. If the no corresponding PWM chip is
available yet, the code returns EPROBE_DEFER to have users automatically
probed again later when the PWM provider may have become available.
Device tree binding documentation is also provided.

Patch 6 was taken from the Chromium tree and is required to provide
proper clocking of the Tegra2 PWFM controller. All Chromium-specific
tags have been removed from the commit message. Some cleanup of the
clock registration for Tegra is done in patch 7 because a subsequent
patch will instantiate one PWFM controller device instead of four.

Patch 8 adds a generic PWM framework driver for the Tegra2 PWFM
controller. The code is taken from the Chromium tree with some
adjustments to integrate it with the PWM framework. Device tree based
probing of the driver is implemented in patch 9.

Patches 10 and 11 are ports of the Blackfin PWM and the PXA PWM drivers
to the PWM framework. These are only compile-tested as I do not have any
hardware to test them on.

Patches 12, 13 and 14 move the i.MX, Samsung and VT8500 drivers over to
the framework. These are originally by Sascha Hauer and were updated to
take advantage of the multiple PWM devices per chip feature. Patch 15
adds support for Freescale MXS by Shawn Guo.

Patch 16 implements DT-based probing in the pwm-backlight driver. Note
that this code only handles the "pwms" property (by looking up the PWM
device via the new PWM DT binding). Switching power to the backlight via
GPIOs is not supported yet. The DT binding also deviates from the
platform data in that it requires a list of brightness levels to be
specified instead of assuming a linearily spaced range from 0 to a given
maximum brightness.

Finally, patch 17 makes me the new maintainer of the PWM subsystem.

The whole series is based on the linux-next tree from 20120405. I think
I've addressed all of the concerns raised in the first five versions. I
have also pushed this version of the series to the PWM subsystem
repository[1].

Thierry

[0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
[1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6

Sascha Hauer (4):
  pwm: Add PWM framework support
  ARM i.MX: Move i.MX pwm driver to pwm framework
  ARM Samsung: Move s3c pwm driver to pwm framework
  ARM vt8500: Move vt8500 pwm driver to pwm framework

Shawn Guo (1):
  pwm: add pwm-mxs support

Simon Que (1):
  ARM: tegra: Fix PWM clock programming

Thierry Reding (11):
  pwm: Allow chips to support multiple PWMs
  pwm: Add debugfs interface
  pwm: Add table-based lookup for static mappings
  pwm: Add device tree support
  ARM: tegra: Provide clock for only one PWM controller
  pwm: Add NVIDIA Tegra SoC support
  pwm: tegra: Add device tree support
  pwm: Move Blackfin PWM driver to PWM framework
  pwm: Move PXA PWM driver to PWM framework
  pwm-backlight: Add rudimentary device tree support
  pwm: Take over maintainership of the PWM subsystem

 Documentation/devicetree/bindings/pwm/mxs-pwm.txt  |   17 +
 .../devicetree/bindings/pwm/nvidia,tegra20-pwm.txt |   18 +
 Documentation/devicetree/bindings/pwm/pwm.txt      |   57 ++
 .../bindings/video/backlight/pwm-backlight         |   28 +
 Documentation/pwm.txt                              |   76 +++
 MAINTAINERS                                        |   12 +
 arch/arm/boot/dts/tegra20.dtsi                     |    6 +
 arch/arm/boot/dts/tegra30.dtsi                     |    6 +
 arch/arm/mach-tegra/board-dt-tegra20.c             |    1 +
 arch/arm/mach-tegra/board-dt-tegra30.c             |    3 +
 arch/arm/mach-tegra/tegra2_clocks.c                |   36 +-
 arch/arm/mach-tegra/tegra30_clocks.c               |    6 +-
 arch/arm/mach-vt8500/Makefile                      |    2 -
 arch/arm/plat-mxc/Kconfig                          |    6 -
 arch/arm/plat-mxc/Makefile                         |    1 -
 arch/arm/plat-pxa/Makefile                         |    1 -
 arch/arm/plat-samsung/Makefile                     |    4 -
 arch/blackfin/Kconfig                              |   10 -
 arch/blackfin/kernel/Makefile                      |    1 -
 drivers/Kconfig                                    |    2 +
 drivers/Makefile                                   |    1 +
 drivers/pwm/Kconfig                                |   76 +++
 drivers/pwm/Makefile                               |    8 +
 drivers/pwm/core.c                                 |  712 ++++++++++++++++++++
 .../kernel/pwm.c => drivers/pwm/pwm-bfin.c         |  138 +++-
 arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c   |  182 ++---
 drivers/pwm/pwm-mxs.c                              |  207 ++++++
 arch/arm/plat-pxa/pwm.c => drivers/pwm/pwm-pxa.c   |  172 ++---
 .../pwm.c => drivers/pwm/pwm-samsung.c             |  228 +++----
 drivers/pwm/pwm-tegra.c                            |  265 ++++++++
 .../mach-vt8500/pwm.c => drivers/pwm/pwm-vt8500.c  |  169 ++---
 drivers/video/backlight/Kconfig                    |    2 +-
 drivers/video/backlight/pwm_bl.c                   |  149 +++-
 include/linux/pwm.h                                |  117 ++++
 include/linux/pwm_backlight.h                      |    1 +
 35 files changed, 2145 insertions(+), 575 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pwm/mxs-pwm.txt
 create mode 100644 Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm.txt
 create mode 100644 Documentation/devicetree/bindings/video/backlight/pwm-backlight
 create mode 100644 Documentation/pwm.txt
 create mode 100644 drivers/pwm/Kconfig
 create mode 100644 drivers/pwm/Makefile
 create mode 100644 drivers/pwm/core.c
 rename arch/blackfin/kernel/pwm.c => drivers/pwm/pwm-bfin.c (26%)
 rename arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c (58%)
 create mode 100644 drivers/pwm/pwm-mxs.c
 rename arch/arm/plat-pxa/pwm.c => drivers/pwm/pwm-pxa.c (57%)
 rename arch/arm/plat-samsung/pwm.c => drivers/pwm/pwm-samsung.c (58%)
 create mode 100644 drivers/pwm/pwm-tegra.c
 rename arch/arm/mach-vt8500/pwm.c => drivers/pwm/pwm-vt8500.c (52%)

-- 
1.7.9.6

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 00/17] Add PWM framework and device tree support
@ 2012-04-10 15:06 ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

This patch series adds very rudimentary device-tree support for PWM
devices. With all of these patches applied (plus one board-specific
patch that is not included), I'm able to control the backlight on the
device I'm working on using the sysfs interface provided by the pwm-bl
driver and the backlight class.

This series is based on Sascha Hauer's series of patches[0] to add a
generic PWM framework. The first patch in this series is taken from
Sascha's branch, while the second patch enables each PWM chip to provide
multiple PWM devices (the Blackfin and PXA drivers have been ported to
the framework for reference). Currently a global namespace is still
provided to keep backwards-compatibility with the legacy PWM API. In
order to achieve this, the number of global PWM devices is limited to
1024. However, patch 2 introduces per-chip indexing of PWM devices in
the core and patch 4 adds an API to request a PWM device using a lookup
table registered by board setup code. They are supposed to eventually
replace pwm_request() and allow to get rid of the global namespace. The
device tree support code does not use the global namespace.

Patch 3 adds a debugfs interface.

Patch 5 adds some code to lookup a PWM chip given its device-tree
handle. This code will be used later on by the pwm-bl driver to find the
PWM device that it should be using. If the no corresponding PWM chip is
available yet, the code returns EPROBE_DEFER to have users automatically
probed again later when the PWM provider may have become available.
Device tree binding documentation is also provided.

Patch 6 was taken from the Chromium tree and is required to provide
proper clocking of the Tegra2 PWFM controller. All Chromium-specific
tags have been removed from the commit message. Some cleanup of the
clock registration for Tegra is done in patch 7 because a subsequent
patch will instantiate one PWFM controller device instead of four.

Patch 8 adds a generic PWM framework driver for the Tegra2 PWFM
controller. The code is taken from the Chromium tree with some
adjustments to integrate it with the PWM framework. Device tree based
probing of the driver is implemented in patch 9.

Patches 10 and 11 are ports of the Blackfin PWM and the PXA PWM drivers
to the PWM framework. These are only compile-tested as I do not have any
hardware to test them on.

Patches 12, 13 and 14 move the i.MX, Samsung and VT8500 drivers over to
the framework. These are originally by Sascha Hauer and were updated to
take advantage of the multiple PWM devices per chip feature. Patch 15
adds support for Freescale MXS by Shawn Guo.

Patch 16 implements DT-based probing in the pwm-backlight driver. Note
that this code only handles the "pwms" property (by looking up the PWM
device via the new PWM DT binding). Switching power to the backlight via
GPIOs is not supported yet. The DT binding also deviates from the
platform data in that it requires a list of brightness levels to be
specified instead of assuming a linearily spaced range from 0 to a given
maximum brightness.

Finally, patch 17 makes me the new maintainer of the PWM subsystem.

The whole series is based on the linux-next tree from 20120405. I think
I've addressed all of the concerns raised in the first five versions. I
have also pushed this version of the series to the PWM subsystem
repository[1].

Thierry

[0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
[1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6

Sascha Hauer (4):
  pwm: Add PWM framework support
  ARM i.MX: Move i.MX pwm driver to pwm framework
  ARM Samsung: Move s3c pwm driver to pwm framework
  ARM vt8500: Move vt8500 pwm driver to pwm framework

Shawn Guo (1):
  pwm: add pwm-mxs support

Simon Que (1):
  ARM: tegra: Fix PWM clock programming

Thierry Reding (11):
  pwm: Allow chips to support multiple PWMs
  pwm: Add debugfs interface
  pwm: Add table-based lookup for static mappings
  pwm: Add device tree support
  ARM: tegra: Provide clock for only one PWM controller
  pwm: Add NVIDIA Tegra SoC support
  pwm: tegra: Add device tree support
  pwm: Move Blackfin PWM driver to PWM framework
  pwm: Move PXA PWM driver to PWM framework
  pwm-backlight: Add rudimentary device tree support
  pwm: Take over maintainership of the PWM subsystem

 Documentation/devicetree/bindings/pwm/mxs-pwm.txt  |   17 +
 .../devicetree/bindings/pwm/nvidia,tegra20-pwm.txt |   18 +
 Documentation/devicetree/bindings/pwm/pwm.txt      |   57 ++
 .../bindings/video/backlight/pwm-backlight         |   28 +
 Documentation/pwm.txt                              |   76 +++
 MAINTAINERS                                        |   12 +
 arch/arm/boot/dts/tegra20.dtsi                     |    6 +
 arch/arm/boot/dts/tegra30.dtsi                     |    6 +
 arch/arm/mach-tegra/board-dt-tegra20.c             |    1 +
 arch/arm/mach-tegra/board-dt-tegra30.c             |    3 +
 arch/arm/mach-tegra/tegra2_clocks.c                |   36 +-
 arch/arm/mach-tegra/tegra30_clocks.c               |    6 +-
 arch/arm/mach-vt8500/Makefile                      |    2 -
 arch/arm/plat-mxc/Kconfig                          |    6 -
 arch/arm/plat-mxc/Makefile                         |    1 -
 arch/arm/plat-pxa/Makefile                         |    1 -
 arch/arm/plat-samsung/Makefile                     |    4 -
 arch/blackfin/Kconfig                              |   10 -
 arch/blackfin/kernel/Makefile                      |    1 -
 drivers/Kconfig                                    |    2 +
 drivers/Makefile                                   |    1 +
 drivers/pwm/Kconfig                                |   76 +++
 drivers/pwm/Makefile                               |    8 +
 drivers/pwm/core.c                                 |  712 ++++++++++++++++++++
 .../kernel/pwm.c => drivers/pwm/pwm-bfin.c         |  138 +++-
 arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c   |  182 ++---
 drivers/pwm/pwm-mxs.c                              |  207 ++++++
 arch/arm/plat-pxa/pwm.c => drivers/pwm/pwm-pxa.c   |  172 ++---
 .../pwm.c => drivers/pwm/pwm-samsung.c             |  228 +++----
 drivers/pwm/pwm-tegra.c                            |  265 ++++++++
 .../mach-vt8500/pwm.c => drivers/pwm/pwm-vt8500.c  |  169 ++---
 drivers/video/backlight/Kconfig                    |    2 +-
 drivers/video/backlight/pwm_bl.c                   |  149 +++-
 include/linux/pwm.h                                |  117 ++++
 include/linux/pwm_backlight.h                      |    1 +
 35 files changed, 2145 insertions(+), 575 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pwm/mxs-pwm.txt
 create mode 100644 Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm.txt
 create mode 100644 Documentation/devicetree/bindings/video/backlight/pwm-backlight
 create mode 100644 Documentation/pwm.txt
 create mode 100644 drivers/pwm/Kconfig
 create mode 100644 drivers/pwm/Makefile
 create mode 100644 drivers/pwm/core.c
 rename arch/blackfin/kernel/pwm.c => drivers/pwm/pwm-bfin.c (26%)
 rename arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c (58%)
 create mode 100644 drivers/pwm/pwm-mxs.c
 rename arch/arm/plat-pxa/pwm.c => drivers/pwm/pwm-pxa.c (57%)
 rename arch/arm/plat-samsung/pwm.c => drivers/pwm/pwm-samsung.c (58%)
 create mode 100644 drivers/pwm/pwm-tegra.c
 rename arch/arm/mach-vt8500/pwm.c => drivers/pwm/pwm-vt8500.c (52%)

-- 
1.7.9.6

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 01/17] pwm: Add PWM framework support
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

From: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

This patch adds framework support for PWM (pulse width modulation) devices.

The is a barebone PWM API already in the kernel under include/linux/pwm.h,
but it does not allow for multiple drivers as each of them implements the
pwm_*() functions.

There are other PWM framework patches around from Bill Gatliff. Unlike
his framework this one does not change the existing API for PWMs so that
this framework can act as a drop in replacement for the existing API.

Why another framework?

Several people argue that there should not be another framework for PWMs
but they should be integrated into one of the existing frameworks like led
or hwmon. Unlike these frameworks the PWM framework is agnostic to the
purpose of the PWM. In fact, a PWM can drive a LED, but this makes the
LED framework a user of a PWM, like already done in leds-pwm.c. The gpio
framework also is not suitable for PWMs. Every gpio could be turned into
a PWM using timer based toggling, but on the other hand not every PWM hardware
device can be turned into a gpio due to the lack of hardware capabilities.

This patch does not try to improve the PWM API yet, this could be done in
subsequent patches.

[thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org: fixup typos, kerneldoc comments]
Signed-off-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Acked-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
Reviewed-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
Reviewed-by: Matthias Kaehlcke <matthias-RprLehDfhQ3k1uMJSBkQmQ@public.gmane.org>
Reviewed-by: Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
Changes in v6:
- some editorial cleanups

Changes in v5:
- minor cleanups and typo fixes
- add some kerneldoc comments

 Documentation/pwm.txt |   54 ++++++++++++
 MAINTAINERS           |    6 ++
 drivers/Kconfig       |    2 +
 drivers/Makefile      |    1 +
 drivers/pwm/Kconfig   |   12 +++
 drivers/pwm/Makefile  |    1 +
 drivers/pwm/core.c    |  227 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pwm.h   |   38 +++++++++
 8 files changed, 341 insertions(+)
 create mode 100644 Documentation/pwm.txt
 create mode 100644 drivers/pwm/Kconfig
 create mode 100644 drivers/pwm/Makefile
 create mode 100644 drivers/pwm/core.c

diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
new file mode 100644
index 0000000..03e39d1
--- /dev/null
+++ b/Documentation/pwm.txt
@@ -0,0 +1,54 @@
+Pulse Width Modulation (PWM) interface
+
+This provides an overview about the Linux PWM interface
+
+PWMs are commonly used for controlling LEDs, fans or vibrators in
+cell phones. PWMs with a fixed purpose have no need implementing
+the Linux PWM API (although they could). However, PWMs are often
+found as discrete devices on SoCs which have no fixed purpose. It's
+up to the board designer to connect them to LEDs or fans. To provide
+this kind of flexibility the generic PWM API exists.
+
+Identifying PWMs
+----------------
+
+Users of the legacy PWM API use unique IDs to refer to PWM devices. One
+goal of the new PWM framework is to get rid of this global namespace.
+
+Using PWMs
+----------
+
+A PWM can be requested using pwm_request() and freed after usage with
+pwm_free(). After being requested a PWM has to be configured using
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
+
+To start/stop toggling the PWM output use pwm_enable()/pwm_disable().
+
+Implementing a PWM driver
+-------------------------
+
+Currently there are two ways to implement pwm drivers. Traditionally
+there only has been the barebone API meaning that each driver has
+to implement the pwm_*() functions itself. This means that it's impossible
+to have multiple PWM drivers in the system. For this reason it's mandatory
+for new drivers to use the generic PWM framework.
+A new PWM device can be added using pwmchip_add() and removed again with
+pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as
+argument which provides the ops and the pwm id to the framework.
+
+Locking
+-------
+
+The PWM core list manipulations are protected by a mutex, so pwm_request()
+and pwm_free() may not be called from an atomic context. Currently the
+PWM core does not enforce any locking to pwm_enable(), pwm_disable() and
+pwm_config(), so the calling context is currently driver specific. This
+is an issue derived from the former barebone API and should be fixed soon.
+
+Helpers
+-------
+
+Currently a PWM can only be configured with period_ns and duty_ns. For several
+use cases freq_hz and duty_percent might be better. Instead of calculating
+this in your driver please consider adding appropriate helpers to the framework.
diff --git a/MAINTAINERS b/MAINTAINERS
index 6454433..275db57 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5420,6 +5420,12 @@ S:	Maintained
 F:	Documentation/video4linux/README.pvrusb2
 F:	drivers/media/video/pvrusb2/
 
+PWM core
+M:	Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
+L:	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S:	Maintained
+F:	drivers/pwm/
+
 PXA2xx/PXA3xx SUPPORT
 M:	Eric Miao <eric.y.miao-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
 M:	Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index b3ae9e5..ec094b5 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -142,4 +142,6 @@ source "drivers/devfreq/Kconfig"
 
 source "drivers/modem_shm/Kconfig"
 
+source "drivers/pwm/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 4855a1a..52fe956 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -8,6 +8,7 @@
 # GPIO must come after pinctrl as gpios may need to mux pins etc
 obj-y				+= pinctrl/
 obj-y				+= gpio/
+obj-y				+= pwm/
 obj-$(CONFIG_PCI)		+= pci/
 obj-$(CONFIG_PARISC)		+= parisc/
 obj-$(CONFIG_RAPIDIO)		+= rapidio/
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
new file mode 100644
index 0000000..93c1052
--- /dev/null
+++ b/drivers/pwm/Kconfig
@@ -0,0 +1,12 @@
+menuconfig PWM
+	bool "PWM Support"
+	help
+	  This enables PWM support through the generic PWM framework.
+	  You only need to enable this, if you also want to enable
+	  one or more of the PWM drivers below.
+
+	  If unsure, say N.
+
+if PWM
+
+endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
new file mode 100644
index 0000000..3469c3d
--- /dev/null
+++ b/drivers/pwm/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PWM)		+= core.o
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
new file mode 100644
index 0000000..0b8a38e
--- /dev/null
+++ b/drivers/pwm/core.c
@@ -0,0 +1,227 @@
+/*
+ * Generic pwmlib implementation
+ *
+ * Copyright (C) 2011 Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
+ *
+ *  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, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+struct pwm_device {
+	struct			pwm_chip *chip;
+	const char		*label;
+	unsigned long		flags;
+#define FLAG_REQUESTED	0
+#define FLAG_ENABLED	1
+	struct list_head	node;
+};
+
+static LIST_HEAD(pwm_list);
+
+static DEFINE_MUTEX(pwm_lock);
+
+static struct pwm_device *_find_pwm(int pwm_id)
+{
+	struct pwm_device *pwm;
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->chip->pwm_id == pwm_id)
+			return pwm;
+	}
+
+	return NULL;
+}
+
+/**
+ * pwmchip_add() - register a new PWM chip
+ * @chip: the PWM chip to add
+ */
+int pwmchip_add(struct pwm_chip *chip)
+{
+	struct pwm_device *pwm;
+	int ret = 0;
+
+	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
+	if (!pwm)
+		return -ENOMEM;
+
+	pwm->chip = chip;
+
+	mutex_lock(&pwm_lock);
+
+	if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	list_add_tail(&pwm->node, &pwm_list);
+out:
+	mutex_unlock(&pwm_lock);
+
+	if (ret)
+		kfree(pwm);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pwmchip_add);
+
+/**
+ * pwmchip_remove() - remove a PWM chip
+ * @chip: the PWM chip to remove
+ *
+ * Removes a PWM chip. This function may return busy if the PWM chip provides
+ * a PWM device that is still requested.
+ */
+int pwmchip_remove(struct pwm_chip *chip)
+{
+	struct pwm_device *pwm;
+	int ret = 0;
+
+	mutex_lock(&pwm_lock);
+
+	pwm = _find_pwm(chip->pwm_id);
+	if (!pwm) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	list_del(&pwm->node);
+
+	kfree(pwm);
+out:
+	mutex_unlock(&pwm_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pwmchip_remove);
+
+/**
+ * pwm_request() - request a PWM device
+ * @pwm_id: global PWM device index
+ * @label: PWM device label
+ */
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int ret;
+
+	mutex_lock(&pwm_lock);
+
+	pwm = _find_pwm(pwm_id);
+	if (!pwm) {
+		pwm = ERR_PTR(-ENOENT);
+		goto out;
+	}
+
+	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
+		pwm = ERR_PTR(-EBUSY);
+		goto out;
+	}
+
+	if (!try_module_get(pwm->chip->ops->owner)) {
+		pwm = ERR_PTR(-ENODEV);
+		goto out;
+	}
+
+	if (pwm->chip->ops->request) {
+		ret = pwm->chip->ops->request(pwm->chip);
+		if (ret) {
+			pwm = ERR_PTR(ret);
+			goto out_put;
+		}
+	}
+
+	pwm->label = label;
+	set_bit(FLAG_REQUESTED, &pwm->flags);
+
+	goto out;
+
+out_put:
+	module_put(pwm->chip->ops->owner);
+out:
+	mutex_unlock(&pwm_lock);
+
+	return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_request);
+
+/**
+ * pwm_free() - free a PWM device
+ * @pwm: PWM device
+ */
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) {
+		pr_warning("PWM device already freed\n");
+		goto out;
+	}
+
+	pwm->label = NULL;
+
+	module_put(pwm->chip->ops->owner);
+out:
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL_GPL(pwm_free);
+
+/**
+ * pwm_config() - change a PWM device configuration
+ * @pwm: PWM device
+ * @duty_ns: "on" time (in nanoseconds)
+ * @period_ns: duration (in nanoseconds) of one cycle
+ */
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns);
+}
+EXPORT_SYMBOL_GPL(pwm_config);
+
+/**
+ * pwm_enable() - start a PWM output toggling
+ * @pwm: PWM device
+ */
+int pwm_enable(struct pwm_device *pwm)
+{
+	if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags))
+		return pwm->chip->ops->enable(pwm->chip);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pwm_enable);
+
+/**
+ * pwm_disable() - stop a PWM output toggling
+ * @pwm: PWM device
+ */
+void pwm_disable(struct pwm_device *pwm)
+{
+	if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags))
+		pwm->chip->ops->disable(pwm->chip);
+}
+EXPORT_SYMBOL_GPL(pwm_disable);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 7c77575..1f308a1 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -28,4 +28,42 @@ int pwm_enable(struct pwm_device *pwm);
  */
 void pwm_disable(struct pwm_device *pwm);
 
+#ifdef CONFIG_PWM
+struct pwm_chip;
+
+/**
+ * struct pwm_ops - PWM controller operations
+ * @request: optional hook for requesting a PWM
+ * @free: optional hook for freeing a PWM
+ * @config: configure duty cycles and period length for this PWM
+ * @enable: enable PWM output toggling
+ * @disable: disable PWM output toggling
+ * @owner: helps prevent removal of modules exporting active PWMs
+ */
+struct pwm_ops {
+	int			(*request)(struct pwm_chip *chip);
+	void			(*free)(struct pwm_chip *chip);
+	int			(*config)(struct pwm_chip *chip, int duty_ns,
+						int period_ns);
+	int			(*enable)(struct pwm_chip *chip);
+	void			(*disable)(struct pwm_chip *chip);
+	struct module		*owner;
+};
+
+/**
+ * struct pwm_chip - abstract a PWM
+ * @pwm_id: global PWM device index
+ * @label: PWM device label
+ * @ops: controller operations
+ */
+struct pwm_chip {
+	int			pwm_id;
+	const char		*label;
+	struct pwm_ops		*ops;
+};
+
+int pwmchip_add(struct pwm_chip *chip);
+int pwmchip_remove(struct pwm_chip *chip);
+#endif
+
 #endif /* __LINUX_PWM_H */
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 01/17] pwm: Add PWM framework support
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

From: Sascha Hauer <s.hauer@pengutronix.de>

This patch adds framework support for PWM (pulse width modulation) devices.

The is a barebone PWM API already in the kernel under include/linux/pwm.h,
but it does not allow for multiple drivers as each of them implements the
pwm_*() functions.

There are other PWM framework patches around from Bill Gatliff. Unlike
his framework this one does not change the existing API for PWMs so that
this framework can act as a drop in replacement for the existing API.

Why another framework?

Several people argue that there should not be another framework for PWMs
but they should be integrated into one of the existing frameworks like led
or hwmon. Unlike these frameworks the PWM framework is agnostic to the
purpose of the PWM. In fact, a PWM can drive a LED, but this makes the
LED framework a user of a PWM, like already done in leds-pwm.c. The gpio
framework also is not suitable for PWMs. Every gpio could be turned into
a PWM using timer based toggling, but on the other hand not every PWM hardware
device can be turned into a gpio due to the lack of hardware capabilities.

This patch does not try to improve the PWM API yet, this could be done in
subsequent patches.

[thierry.reding at avionic-design.de: fixup typos, kerneldoc comments]
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Kurt Van Dijck <kurt.van.dijck@eia.be>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Matthias Kaehlcke <matthias@kaehlcke.net>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
Changes in v6:
- some editorial cleanups

Changes in v5:
- minor cleanups and typo fixes
- add some kerneldoc comments

 Documentation/pwm.txt |   54 ++++++++++++
 MAINTAINERS           |    6 ++
 drivers/Kconfig       |    2 +
 drivers/Makefile      |    1 +
 drivers/pwm/Kconfig   |   12 +++
 drivers/pwm/Makefile  |    1 +
 drivers/pwm/core.c    |  227 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pwm.h   |   38 +++++++++
 8 files changed, 341 insertions(+)
 create mode 100644 Documentation/pwm.txt
 create mode 100644 drivers/pwm/Kconfig
 create mode 100644 drivers/pwm/Makefile
 create mode 100644 drivers/pwm/core.c

diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
new file mode 100644
index 0000000..03e39d1
--- /dev/null
+++ b/Documentation/pwm.txt
@@ -0,0 +1,54 @@
+Pulse Width Modulation (PWM) interface
+
+This provides an overview about the Linux PWM interface
+
+PWMs are commonly used for controlling LEDs, fans or vibrators in
+cell phones. PWMs with a fixed purpose have no need implementing
+the Linux PWM API (although they could). However, PWMs are often
+found as discrete devices on SoCs which have no fixed purpose. It's
+up to the board designer to connect them to LEDs or fans. To provide
+this kind of flexibility the generic PWM API exists.
+
+Identifying PWMs
+----------------
+
+Users of the legacy PWM API use unique IDs to refer to PWM devices. One
+goal of the new PWM framework is to get rid of this global namespace.
+
+Using PWMs
+----------
+
+A PWM can be requested using pwm_request() and freed after usage with
+pwm_free(). After being requested a PWM has to be configured using
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
+
+To start/stop toggling the PWM output use pwm_enable()/pwm_disable().
+
+Implementing a PWM driver
+-------------------------
+
+Currently there are two ways to implement pwm drivers. Traditionally
+there only has been the barebone API meaning that each driver has
+to implement the pwm_*() functions itself. This means that it's impossible
+to have multiple PWM drivers in the system. For this reason it's mandatory
+for new drivers to use the generic PWM framework.
+A new PWM device can be added using pwmchip_add() and removed again with
+pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as
+argument which provides the ops and the pwm id to the framework.
+
+Locking
+-------
+
+The PWM core list manipulations are protected by a mutex, so pwm_request()
+and pwm_free() may not be called from an atomic context. Currently the
+PWM core does not enforce any locking to pwm_enable(), pwm_disable() and
+pwm_config(), so the calling context is currently driver specific. This
+is an issue derived from the former barebone API and should be fixed soon.
+
+Helpers
+-------
+
+Currently a PWM can only be configured with period_ns and duty_ns. For several
+use cases freq_hz and duty_percent might be better. Instead of calculating
+this in your driver please consider adding appropriate helpers to the framework.
diff --git a/MAINTAINERS b/MAINTAINERS
index 6454433..275db57 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5420,6 +5420,12 @@ S:	Maintained
 F:	Documentation/video4linux/README.pvrusb2
 F:	drivers/media/video/pvrusb2/
 
+PWM core
+M:	Sascha Hauer <s.hauer@pengutronix.de>
+L:	linux-kernel at vger.kernel.org
+S:	Maintained
+F:	drivers/pwm/
+
 PXA2xx/PXA3xx SUPPORT
 M:	Eric Miao <eric.y.miao@gmail.com>
 M:	Russell King <linux@arm.linux.org.uk>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index b3ae9e5..ec094b5 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -142,4 +142,6 @@ source "drivers/devfreq/Kconfig"
 
 source "drivers/modem_shm/Kconfig"
 
+source "drivers/pwm/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 4855a1a..52fe956 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -8,6 +8,7 @@
 # GPIO must come after pinctrl as gpios may need to mux pins etc
 obj-y				+= pinctrl/
 obj-y				+= gpio/
+obj-y				+= pwm/
 obj-$(CONFIG_PCI)		+= pci/
 obj-$(CONFIG_PARISC)		+= parisc/
 obj-$(CONFIG_RAPIDIO)		+= rapidio/
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
new file mode 100644
index 0000000..93c1052
--- /dev/null
+++ b/drivers/pwm/Kconfig
@@ -0,0 +1,12 @@
+menuconfig PWM
+	bool "PWM Support"
+	help
+	  This enables PWM support through the generic PWM framework.
+	  You only need to enable this, if you also want to enable
+	  one or more of the PWM drivers below.
+
+	  If unsure, say N.
+
+if PWM
+
+endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
new file mode 100644
index 0000000..3469c3d
--- /dev/null
+++ b/drivers/pwm/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PWM)		+= core.o
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
new file mode 100644
index 0000000..0b8a38e
--- /dev/null
+++ b/drivers/pwm/core.c
@@ -0,0 +1,227 @@
+/*
+ * Generic pwmlib implementation
+ *
+ * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ *  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, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+struct pwm_device {
+	struct			pwm_chip *chip;
+	const char		*label;
+	unsigned long		flags;
+#define FLAG_REQUESTED	0
+#define FLAG_ENABLED	1
+	struct list_head	node;
+};
+
+static LIST_HEAD(pwm_list);
+
+static DEFINE_MUTEX(pwm_lock);
+
+static struct pwm_device *_find_pwm(int pwm_id)
+{
+	struct pwm_device *pwm;
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->chip->pwm_id == pwm_id)
+			return pwm;
+	}
+
+	return NULL;
+}
+
+/**
+ * pwmchip_add() - register a new PWM chip
+ * @chip: the PWM chip to add
+ */
+int pwmchip_add(struct pwm_chip *chip)
+{
+	struct pwm_device *pwm;
+	int ret = 0;
+
+	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
+	if (!pwm)
+		return -ENOMEM;
+
+	pwm->chip = chip;
+
+	mutex_lock(&pwm_lock);
+
+	if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	list_add_tail(&pwm->node, &pwm_list);
+out:
+	mutex_unlock(&pwm_lock);
+
+	if (ret)
+		kfree(pwm);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pwmchip_add);
+
+/**
+ * pwmchip_remove() - remove a PWM chip
+ * @chip: the PWM chip to remove
+ *
+ * Removes a PWM chip. This function may return busy if the PWM chip provides
+ * a PWM device that is still requested.
+ */
+int pwmchip_remove(struct pwm_chip *chip)
+{
+	struct pwm_device *pwm;
+	int ret = 0;
+
+	mutex_lock(&pwm_lock);
+
+	pwm = _find_pwm(chip->pwm_id);
+	if (!pwm) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	list_del(&pwm->node);
+
+	kfree(pwm);
+out:
+	mutex_unlock(&pwm_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pwmchip_remove);
+
+/**
+ * pwm_request() - request a PWM device
+ * @pwm_id: global PWM device index
+ * @label: PWM device label
+ */
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int ret;
+
+	mutex_lock(&pwm_lock);
+
+	pwm = _find_pwm(pwm_id);
+	if (!pwm) {
+		pwm = ERR_PTR(-ENOENT);
+		goto out;
+	}
+
+	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
+		pwm = ERR_PTR(-EBUSY);
+		goto out;
+	}
+
+	if (!try_module_get(pwm->chip->ops->owner)) {
+		pwm = ERR_PTR(-ENODEV);
+		goto out;
+	}
+
+	if (pwm->chip->ops->request) {
+		ret = pwm->chip->ops->request(pwm->chip);
+		if (ret) {
+			pwm = ERR_PTR(ret);
+			goto out_put;
+		}
+	}
+
+	pwm->label = label;
+	set_bit(FLAG_REQUESTED, &pwm->flags);
+
+	goto out;
+
+out_put:
+	module_put(pwm->chip->ops->owner);
+out:
+	mutex_unlock(&pwm_lock);
+
+	return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_request);
+
+/**
+ * pwm_free() - free a PWM device
+ * @pwm: PWM device
+ */
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) {
+		pr_warning("PWM device already freed\n");
+		goto out;
+	}
+
+	pwm->label = NULL;
+
+	module_put(pwm->chip->ops->owner);
+out:
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL_GPL(pwm_free);
+
+/**
+ * pwm_config() - change a PWM device configuration
+ * @pwm: PWM device
+ * @duty_ns: "on" time (in nanoseconds)
+ * @period_ns: duration (in nanoseconds) of one cycle
+ */
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns);
+}
+EXPORT_SYMBOL_GPL(pwm_config);
+
+/**
+ * pwm_enable() - start a PWM output toggling
+ * @pwm: PWM device
+ */
+int pwm_enable(struct pwm_device *pwm)
+{
+	if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags))
+		return pwm->chip->ops->enable(pwm->chip);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pwm_enable);
+
+/**
+ * pwm_disable() - stop a PWM output toggling
+ * @pwm: PWM device
+ */
+void pwm_disable(struct pwm_device *pwm)
+{
+	if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags))
+		pwm->chip->ops->disable(pwm->chip);
+}
+EXPORT_SYMBOL_GPL(pwm_disable);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 7c77575..1f308a1 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -28,4 +28,42 @@ int pwm_enable(struct pwm_device *pwm);
  */
 void pwm_disable(struct pwm_device *pwm);
 
+#ifdef CONFIG_PWM
+struct pwm_chip;
+
+/**
+ * struct pwm_ops - PWM controller operations
+ * @request: optional hook for requesting a PWM
+ * @free: optional hook for freeing a PWM
+ * @config: configure duty cycles and period length for this PWM
+ * @enable: enable PWM output toggling
+ * @disable: disable PWM output toggling
+ * @owner: helps prevent removal of modules exporting active PWMs
+ */
+struct pwm_ops {
+	int			(*request)(struct pwm_chip *chip);
+	void			(*free)(struct pwm_chip *chip);
+	int			(*config)(struct pwm_chip *chip, int duty_ns,
+						int period_ns);
+	int			(*enable)(struct pwm_chip *chip);
+	void			(*disable)(struct pwm_chip *chip);
+	struct module		*owner;
+};
+
+/**
+ * struct pwm_chip - abstract a PWM
+ * @pwm_id: global PWM device index
+ * @label: PWM device label
+ * @ops: controller operations
+ */
+struct pwm_chip {
+	int			pwm_id;
+	const char		*label;
+	struct pwm_ops		*ops;
+};
+
+int pwmchip_add(struct pwm_chip *chip);
+int pwmchip_remove(struct pwm_chip *chip);
+#endif
+
 #endif /* __LINUX_PWM_H */
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 02/17] pwm: Allow chips to support multiple PWMs
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

Many PWM controllers provide access to more than a single PWM output and
may even share some resource among them. Allowing a PWM chip to provide
multiple PWM devices enables better sharing of those resources. As a
side-effect this change allows easy integration with the device tree
where a given PWM can be looked up based on the PWM chip's phandle and a
corresponding index.

This commit modifies the PWM core to support multiple PWMs per struct
pwm_chip. It achieves this in a similar way to how gpiolib works, by
allowing PWM ranges to be requested dynamically (pwm_chip.base == -1) or
starting at a given offset (pwm_chip.base >= 0). A chip specifies how
many PWMs it controls using the npwm member. Each of the functions in
the pwm_ops structure gets an additional argument that specified the PWM
number (it can be converted to a per-chip index by subtracting the
chip's base).

The total maximum number of PWM devices is currently fixed to 1024 while
the data is actually stored in a radix tree, thus saving resources if
not all of them are used.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
Reviewed-by: Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
Changes in v5:
- add sanity checks to pwmchip_add()
- make pwm_chip.pwm_ops constant
- remove unused pwm_request_from_device() function
- check for valid chip in pwm_request_from_chip()
- add sanity checks to pwm_config(), pwm_enable() and pwm_disable()
- update Documentation/pwm.txt
- use deferred driver probing if pwm_request() cannot find a PWM

Changes in v4:
- remove OF-specific code to preserve bisectability
- add pwm_request_from_device() and pwm_request_from_chip() functions
- refactor PWM requesting code
- store period in struct pwm_device and get rid of the unused struct
  pwm_spec

Changes in v3:
- get rid of pwm_desc structure and keep only struct pwm_device
- keep a list of pwm_chip structures for fast and easy lookup
- pass struct pwm_device directly to pwm_ops
- add debugfs file

Changes in v2:
- add 'struct device *dev' field to pwm_chip, drop label field
- use radix tree for PWM descriptors
- add pwm_set_chip_data() and pwm_get_chip_data() functions to allow
  storing and retrieving chip-specific per-PWM data

 Documentation/pwm.txt |    9 +-
 drivers/pwm/core.c    |  263 ++++++++++++++++++++++++++++++++++---------------
 include/linux/pwm.h   |   71 ++++++++++---
 3 files changed, 250 insertions(+), 93 deletions(-)

diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 03e39d1..48f598a 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -33,9 +33,12 @@ there only has been the barebone API meaning that each driver has
 to implement the pwm_*() functions itself. This means that it's impossible
 to have multiple PWM drivers in the system. For this reason it's mandatory
 for new drivers to use the generic PWM framework.
-A new PWM device can be added using pwmchip_add() and removed again with
-pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as
-argument which provides the ops and the pwm id to the framework.
+
+A new PWM controller/chip can be added using pwmchip_add() and removed
+again with pwmchip_remove(). pwmchip_add() takes a filled in struct
+pwm_chip as argument which provides a description of the PWM chip, the
+number of PWM devices provider by the chip and the chip-specific
+implementation of the supported PWM operations to the framework.
 
 Locking
 -------
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 0b8a38e..8688754 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -2,6 +2,7 @@
  * Generic pwmlib implementation
  *
  * Copyright (C) 2011 Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
+ * Copyright (C) 2011-2012 Avionic Design GmbH
  *
  *  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
@@ -20,66 +21,157 @@
 
 #include <linux/module.h>
 #include <linux/pwm.h>
+#include <linux/radix-tree.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/device.h>
 
-struct pwm_device {
-	struct			pwm_chip *chip;
-	const char		*label;
-	unsigned long		flags;
-#define FLAG_REQUESTED	0
-#define FLAG_ENABLED	1
-	struct list_head	node;
-};
-
-static LIST_HEAD(pwm_list);
+#define MAX_PWMS 1024
 
 static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_chips);
+static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
+static RADIX_TREE(pwm_tree, GFP_KERNEL);
 
-static struct pwm_device *_find_pwm(int pwm_id)
+static struct pwm_device *pwm_to_device(unsigned int pwm)
 {
-	struct pwm_device *pwm;
+	return radix_tree_lookup(&pwm_tree, pwm);
+}
+
+static int alloc_pwms(int pwm, unsigned int count)
+{
+	unsigned int from = 0;
+	unsigned int start;
+
+	if (pwm >= MAX_PWMS)
+		return -EINVAL;
+
+	if (pwm >= 0)
+		from = pwm;
+
+	start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from,
+					   count, 0);
+
+	if (pwm >= 0 && start != pwm)
+		return -EEXIST;
+
+	if (start + count > MAX_PWMS)
+		return -ENOSPC;
+
+	return start;
+}
 
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->chip->pwm_id == pwm_id)
-			return pwm;
+static void free_pwms(struct pwm_chip *chip)
+{
+	unsigned int i;
+
+	for (i = 0; i < chip->npwm; i++) {
+		struct pwm_device *pwm = &chip->pwms[i];
+		radix_tree_delete(&pwm_tree, pwm->pwm);
+	}
+
+	bitmap_clear(allocated_pwms, chip->base, chip->npwm);
+
+	kfree(chip->pwms);
+	chip->pwms = NULL;
+}
+
+static int pwm_device_request(struct pwm_device *pwm, const char *label)
+{
+	int err;
+
+	if (test_bit(PWMF_REQUESTED, &pwm->flags))
+		return -EBUSY;
+
+	if (!try_module_get(pwm->chip->ops->owner))
+		return -ENODEV;
+
+	if (pwm->chip->ops->request) {
+		err = pwm->chip->ops->request(pwm->chip, pwm);
+		if (err) {
+			module_put(pwm->chip->ops->owner);
+			return err;
+		}
 	}
 
-	return NULL;
+	set_bit(PWMF_REQUESTED, &pwm->flags);
+	pwm->label = label;
+
+	return 0;
+}
+
+/**
+ * pwm_set_chip_data() - set private chip data for a PWM
+ * @pwm: PWM device
+ * @data: pointer to chip-specific data
+ */
+int pwm_set_chip_data(struct pwm_device *pwm, void *data)
+{
+	if (!pwm)
+		return -EINVAL;
+
+	pwm->chip_data = data;
+
+	return 0;
+}
+
+/**
+ * pwm_get_chip_data() - get private chip data for a PWM
+ * @pwm: PWM device
+ */
+void *pwm_get_chip_data(struct pwm_device *pwm)
+{
+	return pwm ? pwm->chip_data : NULL;
 }
 
 /**
  * pwmchip_add() - register a new PWM chip
  * @chip: the PWM chip to add
+ *
+ * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
+ * will be used.
  */
 int pwmchip_add(struct pwm_chip *chip)
 {
 	struct pwm_device *pwm;
-	int ret = 0;
-
-	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
-	if (!pwm)
-		return -ENOMEM;
+	unsigned int i;
+	int ret;
 
-	pwm->chip = chip;
+	if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
+	    !chip->ops->enable || !chip->ops->disable)
+		return -EINVAL;
 
 	mutex_lock(&pwm_lock);
 
-	if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) {
-		ret = -EBUSY;
+	ret = alloc_pwms(chip->base, chip->npwm);
+	if (ret < 0)
 		goto out;
+
+	chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL);
+	if (!chip->pwms)
+		return -ENOMEM;
+
+	chip->base = ret;
+
+	for (i = 0; i < chip->npwm; i++) {
+		pwm = &chip->pwms[i];
+
+		pwm->chip = chip;
+		pwm->pwm = chip->base + i;
+		pwm->hwpwm = i;
+
+		radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
 	}
 
-	list_add_tail(&pwm->node, &pwm_list);
-out:
-	mutex_unlock(&pwm_lock);
+	bitmap_set(allocated_pwms, chip->base, chip->npwm);
 
-	if (ret)
-		kfree(pwm);
+	INIT_LIST_HEAD(&chip->list);
+	list_add(&chip->list, &pwm_chips);
 
+out:
+	mutex_unlock(&pwm_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(pwmchip_add);
@@ -93,28 +185,25 @@ EXPORT_SYMBOL_GPL(pwmchip_add);
  */
 int pwmchip_remove(struct pwm_chip *chip)
 {
-	struct pwm_device *pwm;
+	unsigned int i;
 	int ret = 0;
 
 	mutex_lock(&pwm_lock);
 
-	pwm = _find_pwm(chip->pwm_id);
-	if (!pwm) {
-		ret = -ENOENT;
-		goto out;
-	}
+	for (i = 0; i < chip->npwm; i++) {
+		struct pwm_device *pwm = &chip->pwms[i];
 
-	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
-		ret = -EBUSY;
-		goto out;
+		if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
+			ret = -EBUSY;
+			goto out;
+		}
 	}
 
-	list_del(&pwm->node);
+	list_del_init(&chip->list);
+	free_pwms(chip);
 
-	kfree(pwm);
 out:
 	mutex_unlock(&pwm_lock);
-
 	return ret;
 }
 EXPORT_SYMBOL_GPL(pwmchip_remove);
@@ -124,50 +213,64 @@ EXPORT_SYMBOL_GPL(pwmchip_remove);
  * @pwm_id: global PWM device index
  * @label: PWM device label
  */
-struct pwm_device *pwm_request(int pwm_id, const char *label)
+struct pwm_device *pwm_request(int pwm, const char *label)
 {
-	struct pwm_device *pwm;
-	int ret;
+	struct pwm_device *dev;
+	int err;
+
+	if (pwm < 0 || pwm >= MAX_PWMS)
+		return ERR_PTR(-EINVAL);
 
 	mutex_lock(&pwm_lock);
 
-	pwm = _find_pwm(pwm_id);
-	if (!pwm) {
-		pwm = ERR_PTR(-ENOENT);
+	dev = pwm_to_device(pwm);
+	if (!dev) {
+		dev = ERR_PTR(-EPROBE_DEFER);
 		goto out;
 	}
 
-	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
-		pwm = ERR_PTR(-EBUSY);
-		goto out;
-	}
+	err = pwm_device_request(dev, label);
+	if (err < 0)
+		dev = ERR_PTR(err);
 
-	if (!try_module_get(pwm->chip->ops->owner)) {
-		pwm = ERR_PTR(-ENODEV);
-		goto out;
-	}
+out:
+	mutex_unlock(&pwm_lock);
 
-	if (pwm->chip->ops->request) {
-		ret = pwm->chip->ops->request(pwm->chip);
-		if (ret) {
-			pwm = ERR_PTR(ret);
-			goto out_put;
-		}
-	}
+	return dev;
+}
+EXPORT_SYMBOL_GPL(pwm_request);
 
-	pwm->label = label;
-	set_bit(FLAG_REQUESTED, &pwm->flags);
+/**
+ * pwm_request_from_chip() - request a PWM device relative to a PWM chip
+ * @chip: PWM chip
+ * @index: per-chip index of the PWM to request
+ * @label: a literal description string of this PWM
+ *
+ * Returns the PWM at the given index of the given PWM chip. A negative error
+ * code is returned if the index is not valid for the specified PWM chip or
+ * if the PWM device cannot be requested.
+ */
+struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
+					 unsigned int index,
+					 const char *label)
+{
+	struct pwm_device *pwm;
+	int err;
 
-	goto out;
+	if (!chip || index >= chip->npwm)
+		return ERR_PTR(-EINVAL);
 
-out_put:
-	module_put(pwm->chip->ops->owner);
-out:
-	mutex_unlock(&pwm_lock);
+	mutex_lock(&pwm_lock);
+	pwm = &chip->pwms[index];
 
+	err = pwm_device_request(pwm, label);
+	if (err < 0)
+		pwm = ERR_PTR(err);
+
+	mutex_unlock(&pwm_lock);
 	return pwm;
 }
-EXPORT_SYMBOL_GPL(pwm_request);
+EXPORT_SYMBOL_GPL(pwm_request_from_chip);
 
 /**
  * pwm_free() - free a PWM device
@@ -177,11 +280,14 @@ void pwm_free(struct pwm_device *pwm)
 {
 	mutex_lock(&pwm_lock);
 
-	if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) {
+	if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
 		pr_warning("PWM device already freed\n");
 		goto out;
 	}
 
+	if (pwm->chip->ops->free)
+		pwm->chip->ops->free(pwm->chip, pwm);
+
 	pwm->label = NULL;
 
 	module_put(pwm->chip->ops->owner);
@@ -198,7 +304,10 @@ EXPORT_SYMBOL_GPL(pwm_free);
  */
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
-	return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns);
+	if (!pwm || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
 }
 EXPORT_SYMBOL_GPL(pwm_config);
 
@@ -208,10 +317,10 @@ EXPORT_SYMBOL_GPL(pwm_config);
  */
 int pwm_enable(struct pwm_device *pwm)
 {
-	if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags))
-		return pwm->chip->ops->enable(pwm->chip);
+	if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags))
+		return pwm->chip->ops->enable(pwm->chip, pwm);
 
-	return 0;
+	return pwm ? 0 : -EINVAL;
 }
 EXPORT_SYMBOL_GPL(pwm_enable);
 
@@ -221,7 +330,7 @@ EXPORT_SYMBOL_GPL(pwm_enable);
  */
 void pwm_disable(struct pwm_device *pwm)
 {
-	if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags))
-		pwm->chip->ops->disable(pwm->chip);
+	if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags))
+		pwm->chip->ops->disable(pwm->chip, pwm);
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 1f308a1..5710391 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -31,6 +31,33 @@ void pwm_disable(struct pwm_device *pwm);
 #ifdef CONFIG_PWM
 struct pwm_chip;
 
+enum {
+	PWMF_REQUESTED = 1 << 0,
+	PWMF_ENABLED = 1 << 1,
+};
+
+struct pwm_device {
+	const char		*label;
+	unsigned long		flags;
+	unsigned int		hwpwm;
+	unsigned int		pwm;
+	struct pwm_chip		*chip;
+	void			*chip_data;
+
+	unsigned int		period; /* in nanoseconds */
+};
+
+static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
+{
+	if (pwm)
+		pwm->period = period;
+}
+
+static inline unsigned int pwm_get_period(struct pwm_device *pwm)
+{
+	return pwm ? pwm->period : 0;
+}
+
 /**
  * struct pwm_ops - PWM controller operations
  * @request: optional hook for requesting a PWM
@@ -41,29 +68,47 @@ struct pwm_chip;
  * @owner: helps prevent removal of modules exporting active PWMs
  */
 struct pwm_ops {
-	int			(*request)(struct pwm_chip *chip);
-	void			(*free)(struct pwm_chip *chip);
-	int			(*config)(struct pwm_chip *chip, int duty_ns,
-						int period_ns);
-	int			(*enable)(struct pwm_chip *chip);
-	void			(*disable)(struct pwm_chip *chip);
+	int			(*request)(struct pwm_chip *chip,
+					   struct pwm_device *pwm);
+	void			(*free)(struct pwm_chip *chip,
+					struct pwm_device *pwm);
+	int			(*config)(struct pwm_chip *chip,
+					  struct pwm_device *pwm,
+					  int duty_ns, int period_ns);
+	int			(*enable)(struct pwm_chip *chip,
+					  struct pwm_device *pwm);
+	void			(*disable)(struct pwm_chip *chip,
+					   struct pwm_device *pwm);
 	struct module		*owner;
 };
 
 /**
- * struct pwm_chip - abstract a PWM
- * @pwm_id: global PWM device index
- * @label: PWM device label
- * @ops: controller operations
+ * struct pwm_chip - abstract a PWM controller
+ * @dev: device providing the PWMs
+ * @list: list node for internal use
+ * @ops: callbacks for this PWM controller
+ * @base: number of first PWM controlled by this chip
+ * @npwm: number of PWMs controlled by this chip
+ * @pwms: array of PWM devices allocated by the framework
  */
 struct pwm_chip {
-	int			pwm_id;
-	const char		*label;
-	struct pwm_ops		*ops;
+	struct device		*dev;
+	struct list_head	list;
+	const struct pwm_ops	*ops;
+	int			base;
+	unsigned int		npwm;
+
+	struct pwm_device	*pwms;
 };
 
+int pwm_set_chip_data(struct pwm_device *pwm, void *data);
+void *pwm_get_chip_data(struct pwm_device *pwm);
+
 int pwmchip_add(struct pwm_chip *chip);
 int pwmchip_remove(struct pwm_chip *chip);
+struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
+					 unsigned int index,
+					 const char *label);
 #endif
 
 #endif /* __LINUX_PWM_H */
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 02/17] pwm: Allow chips to support multiple PWMs
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

Many PWM controllers provide access to more than a single PWM output and
may even share some resource among them. Allowing a PWM chip to provide
multiple PWM devices enables better sharing of those resources. As a
side-effect this change allows easy integration with the device tree
where a given PWM can be looked up based on the PWM chip's phandle and a
corresponding index.

This commit modifies the PWM core to support multiple PWMs per struct
pwm_chip. It achieves this in a similar way to how gpiolib works, by
allowing PWM ranges to be requested dynamically (pwm_chip.base == -1) or
starting at a given offset (pwm_chip.base >= 0). A chip specifies how
many PWMs it controls using the npwm member. Each of the functions in
the pwm_ops structure gets an additional argument that specified the PWM
number (it can be converted to a per-chip index by subtracting the
chip's base).

The total maximum number of PWM devices is currently fixed to 1024 while
the data is actually stored in a radix tree, thus saving resources if
not all of them are used.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
---
Changes in v5:
- add sanity checks to pwmchip_add()
- make pwm_chip.pwm_ops constant
- remove unused pwm_request_from_device() function
- check for valid chip in pwm_request_from_chip()
- add sanity checks to pwm_config(), pwm_enable() and pwm_disable()
- update Documentation/pwm.txt
- use deferred driver probing if pwm_request() cannot find a PWM

Changes in v4:
- remove OF-specific code to preserve bisectability
- add pwm_request_from_device() and pwm_request_from_chip() functions
- refactor PWM requesting code
- store period in struct pwm_device and get rid of the unused struct
  pwm_spec

Changes in v3:
- get rid of pwm_desc structure and keep only struct pwm_device
- keep a list of pwm_chip structures for fast and easy lookup
- pass struct pwm_device directly to pwm_ops
- add debugfs file

Changes in v2:
- add 'struct device *dev' field to pwm_chip, drop label field
- use radix tree for PWM descriptors
- add pwm_set_chip_data() and pwm_get_chip_data() functions to allow
  storing and retrieving chip-specific per-PWM data

 Documentation/pwm.txt |    9 +-
 drivers/pwm/core.c    |  263 ++++++++++++++++++++++++++++++++++---------------
 include/linux/pwm.h   |   71 ++++++++++---
 3 files changed, 250 insertions(+), 93 deletions(-)

diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 03e39d1..48f598a 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -33,9 +33,12 @@ there only has been the barebone API meaning that each driver has
 to implement the pwm_*() functions itself. This means that it's impossible
 to have multiple PWM drivers in the system. For this reason it's mandatory
 for new drivers to use the generic PWM framework.
-A new PWM device can be added using pwmchip_add() and removed again with
-pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as
-argument which provides the ops and the pwm id to the framework.
+
+A new PWM controller/chip can be added using pwmchip_add() and removed
+again with pwmchip_remove(). pwmchip_add() takes a filled in struct
+pwm_chip as argument which provides a description of the PWM chip, the
+number of PWM devices provider by the chip and the chip-specific
+implementation of the supported PWM operations to the framework.
 
 Locking
 -------
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 0b8a38e..8688754 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -2,6 +2,7 @@
  * Generic pwmlib implementation
  *
  * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2011-2012 Avionic Design GmbH
  *
  *  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
@@ -20,66 +21,157 @@
 
 #include <linux/module.h>
 #include <linux/pwm.h>
+#include <linux/radix-tree.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/device.h>
 
-struct pwm_device {
-	struct			pwm_chip *chip;
-	const char		*label;
-	unsigned long		flags;
-#define FLAG_REQUESTED	0
-#define FLAG_ENABLED	1
-	struct list_head	node;
-};
-
-static LIST_HEAD(pwm_list);
+#define MAX_PWMS 1024
 
 static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_chips);
+static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
+static RADIX_TREE(pwm_tree, GFP_KERNEL);
 
-static struct pwm_device *_find_pwm(int pwm_id)
+static struct pwm_device *pwm_to_device(unsigned int pwm)
 {
-	struct pwm_device *pwm;
+	return radix_tree_lookup(&pwm_tree, pwm);
+}
+
+static int alloc_pwms(int pwm, unsigned int count)
+{
+	unsigned int from = 0;
+	unsigned int start;
+
+	if (pwm >= MAX_PWMS)
+		return -EINVAL;
+
+	if (pwm >= 0)
+		from = pwm;
+
+	start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from,
+					   count, 0);
+
+	if (pwm >= 0 && start != pwm)
+		return -EEXIST;
+
+	if (start + count > MAX_PWMS)
+		return -ENOSPC;
+
+	return start;
+}
 
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->chip->pwm_id == pwm_id)
-			return pwm;
+static void free_pwms(struct pwm_chip *chip)
+{
+	unsigned int i;
+
+	for (i = 0; i < chip->npwm; i++) {
+		struct pwm_device *pwm = &chip->pwms[i];
+		radix_tree_delete(&pwm_tree, pwm->pwm);
+	}
+
+	bitmap_clear(allocated_pwms, chip->base, chip->npwm);
+
+	kfree(chip->pwms);
+	chip->pwms = NULL;
+}
+
+static int pwm_device_request(struct pwm_device *pwm, const char *label)
+{
+	int err;
+
+	if (test_bit(PWMF_REQUESTED, &pwm->flags))
+		return -EBUSY;
+
+	if (!try_module_get(pwm->chip->ops->owner))
+		return -ENODEV;
+
+	if (pwm->chip->ops->request) {
+		err = pwm->chip->ops->request(pwm->chip, pwm);
+		if (err) {
+			module_put(pwm->chip->ops->owner);
+			return err;
+		}
 	}
 
-	return NULL;
+	set_bit(PWMF_REQUESTED, &pwm->flags);
+	pwm->label = label;
+
+	return 0;
+}
+
+/**
+ * pwm_set_chip_data() - set private chip data for a PWM
+ * @pwm: PWM device
+ * @data: pointer to chip-specific data
+ */
+int pwm_set_chip_data(struct pwm_device *pwm, void *data)
+{
+	if (!pwm)
+		return -EINVAL;
+
+	pwm->chip_data = data;
+
+	return 0;
+}
+
+/**
+ * pwm_get_chip_data() - get private chip data for a PWM
+ * @pwm: PWM device
+ */
+void *pwm_get_chip_data(struct pwm_device *pwm)
+{
+	return pwm ? pwm->chip_data : NULL;
 }
 
 /**
  * pwmchip_add() - register a new PWM chip
  * @chip: the PWM chip to add
+ *
+ * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
+ * will be used.
  */
 int pwmchip_add(struct pwm_chip *chip)
 {
 	struct pwm_device *pwm;
-	int ret = 0;
-
-	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
-	if (!pwm)
-		return -ENOMEM;
+	unsigned int i;
+	int ret;
 
-	pwm->chip = chip;
+	if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
+	    !chip->ops->enable || !chip->ops->disable)
+		return -EINVAL;
 
 	mutex_lock(&pwm_lock);
 
-	if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) {
-		ret = -EBUSY;
+	ret = alloc_pwms(chip->base, chip->npwm);
+	if (ret < 0)
 		goto out;
+
+	chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL);
+	if (!chip->pwms)
+		return -ENOMEM;
+
+	chip->base = ret;
+
+	for (i = 0; i < chip->npwm; i++) {
+		pwm = &chip->pwms[i];
+
+		pwm->chip = chip;
+		pwm->pwm = chip->base + i;
+		pwm->hwpwm = i;
+
+		radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
 	}
 
-	list_add_tail(&pwm->node, &pwm_list);
-out:
-	mutex_unlock(&pwm_lock);
+	bitmap_set(allocated_pwms, chip->base, chip->npwm);
 
-	if (ret)
-		kfree(pwm);
+	INIT_LIST_HEAD(&chip->list);
+	list_add(&chip->list, &pwm_chips);
 
+out:
+	mutex_unlock(&pwm_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(pwmchip_add);
@@ -93,28 +185,25 @@ EXPORT_SYMBOL_GPL(pwmchip_add);
  */
 int pwmchip_remove(struct pwm_chip *chip)
 {
-	struct pwm_device *pwm;
+	unsigned int i;
 	int ret = 0;
 
 	mutex_lock(&pwm_lock);
 
-	pwm = _find_pwm(chip->pwm_id);
-	if (!pwm) {
-		ret = -ENOENT;
-		goto out;
-	}
+	for (i = 0; i < chip->npwm; i++) {
+		struct pwm_device *pwm = &chip->pwms[i];
 
-	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
-		ret = -EBUSY;
-		goto out;
+		if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
+			ret = -EBUSY;
+			goto out;
+		}
 	}
 
-	list_del(&pwm->node);
+	list_del_init(&chip->list);
+	free_pwms(chip);
 
-	kfree(pwm);
 out:
 	mutex_unlock(&pwm_lock);
-
 	return ret;
 }
 EXPORT_SYMBOL_GPL(pwmchip_remove);
@@ -124,50 +213,64 @@ EXPORT_SYMBOL_GPL(pwmchip_remove);
  * @pwm_id: global PWM device index
  * @label: PWM device label
  */
-struct pwm_device *pwm_request(int pwm_id, const char *label)
+struct pwm_device *pwm_request(int pwm, const char *label)
 {
-	struct pwm_device *pwm;
-	int ret;
+	struct pwm_device *dev;
+	int err;
+
+	if (pwm < 0 || pwm >= MAX_PWMS)
+		return ERR_PTR(-EINVAL);
 
 	mutex_lock(&pwm_lock);
 
-	pwm = _find_pwm(pwm_id);
-	if (!pwm) {
-		pwm = ERR_PTR(-ENOENT);
+	dev = pwm_to_device(pwm);
+	if (!dev) {
+		dev = ERR_PTR(-EPROBE_DEFER);
 		goto out;
 	}
 
-	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
-		pwm = ERR_PTR(-EBUSY);
-		goto out;
-	}
+	err = pwm_device_request(dev, label);
+	if (err < 0)
+		dev = ERR_PTR(err);
 
-	if (!try_module_get(pwm->chip->ops->owner)) {
-		pwm = ERR_PTR(-ENODEV);
-		goto out;
-	}
+out:
+	mutex_unlock(&pwm_lock);
 
-	if (pwm->chip->ops->request) {
-		ret = pwm->chip->ops->request(pwm->chip);
-		if (ret) {
-			pwm = ERR_PTR(ret);
-			goto out_put;
-		}
-	}
+	return dev;
+}
+EXPORT_SYMBOL_GPL(pwm_request);
 
-	pwm->label = label;
-	set_bit(FLAG_REQUESTED, &pwm->flags);
+/**
+ * pwm_request_from_chip() - request a PWM device relative to a PWM chip
+ * @chip: PWM chip
+ * @index: per-chip index of the PWM to request
+ * @label: a literal description string of this PWM
+ *
+ * Returns the PWM at the given index of the given PWM chip. A negative error
+ * code is returned if the index is not valid for the specified PWM chip or
+ * if the PWM device cannot be requested.
+ */
+struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
+					 unsigned int index,
+					 const char *label)
+{
+	struct pwm_device *pwm;
+	int err;
 
-	goto out;
+	if (!chip || index >= chip->npwm)
+		return ERR_PTR(-EINVAL);
 
-out_put:
-	module_put(pwm->chip->ops->owner);
-out:
-	mutex_unlock(&pwm_lock);
+	mutex_lock(&pwm_lock);
+	pwm = &chip->pwms[index];
 
+	err = pwm_device_request(pwm, label);
+	if (err < 0)
+		pwm = ERR_PTR(err);
+
+	mutex_unlock(&pwm_lock);
 	return pwm;
 }
-EXPORT_SYMBOL_GPL(pwm_request);
+EXPORT_SYMBOL_GPL(pwm_request_from_chip);
 
 /**
  * pwm_free() - free a PWM device
@@ -177,11 +280,14 @@ void pwm_free(struct pwm_device *pwm)
 {
 	mutex_lock(&pwm_lock);
 
-	if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) {
+	if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
 		pr_warning("PWM device already freed\n");
 		goto out;
 	}
 
+	if (pwm->chip->ops->free)
+		pwm->chip->ops->free(pwm->chip, pwm);
+
 	pwm->label = NULL;
 
 	module_put(pwm->chip->ops->owner);
@@ -198,7 +304,10 @@ EXPORT_SYMBOL_GPL(pwm_free);
  */
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
-	return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns);
+	if (!pwm || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
 }
 EXPORT_SYMBOL_GPL(pwm_config);
 
@@ -208,10 +317,10 @@ EXPORT_SYMBOL_GPL(pwm_config);
  */
 int pwm_enable(struct pwm_device *pwm)
 {
-	if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags))
-		return pwm->chip->ops->enable(pwm->chip);
+	if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags))
+		return pwm->chip->ops->enable(pwm->chip, pwm);
 
-	return 0;
+	return pwm ? 0 : -EINVAL;
 }
 EXPORT_SYMBOL_GPL(pwm_enable);
 
@@ -221,7 +330,7 @@ EXPORT_SYMBOL_GPL(pwm_enable);
  */
 void pwm_disable(struct pwm_device *pwm)
 {
-	if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags))
-		pwm->chip->ops->disable(pwm->chip);
+	if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags))
+		pwm->chip->ops->disable(pwm->chip, pwm);
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 1f308a1..5710391 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -31,6 +31,33 @@ void pwm_disable(struct pwm_device *pwm);
 #ifdef CONFIG_PWM
 struct pwm_chip;
 
+enum {
+	PWMF_REQUESTED = 1 << 0,
+	PWMF_ENABLED = 1 << 1,
+};
+
+struct pwm_device {
+	const char		*label;
+	unsigned long		flags;
+	unsigned int		hwpwm;
+	unsigned int		pwm;
+	struct pwm_chip		*chip;
+	void			*chip_data;
+
+	unsigned int		period; /* in nanoseconds */
+};
+
+static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
+{
+	if (pwm)
+		pwm->period = period;
+}
+
+static inline unsigned int pwm_get_period(struct pwm_device *pwm)
+{
+	return pwm ? pwm->period : 0;
+}
+
 /**
  * struct pwm_ops - PWM controller operations
  * @request: optional hook for requesting a PWM
@@ -41,29 +68,47 @@ struct pwm_chip;
  * @owner: helps prevent removal of modules exporting active PWMs
  */
 struct pwm_ops {
-	int			(*request)(struct pwm_chip *chip);
-	void			(*free)(struct pwm_chip *chip);
-	int			(*config)(struct pwm_chip *chip, int duty_ns,
-						int period_ns);
-	int			(*enable)(struct pwm_chip *chip);
-	void			(*disable)(struct pwm_chip *chip);
+	int			(*request)(struct pwm_chip *chip,
+					   struct pwm_device *pwm);
+	void			(*free)(struct pwm_chip *chip,
+					struct pwm_device *pwm);
+	int			(*config)(struct pwm_chip *chip,
+					  struct pwm_device *pwm,
+					  int duty_ns, int period_ns);
+	int			(*enable)(struct pwm_chip *chip,
+					  struct pwm_device *pwm);
+	void			(*disable)(struct pwm_chip *chip,
+					   struct pwm_device *pwm);
 	struct module		*owner;
 };
 
 /**
- * struct pwm_chip - abstract a PWM
- * @pwm_id: global PWM device index
- * @label: PWM device label
- * @ops: controller operations
+ * struct pwm_chip - abstract a PWM controller
+ * @dev: device providing the PWMs
+ * @list: list node for internal use
+ * @ops: callbacks for this PWM controller
+ * @base: number of first PWM controlled by this chip
+ * @npwm: number of PWMs controlled by this chip
+ * @pwms: array of PWM devices allocated by the framework
  */
 struct pwm_chip {
-	int			pwm_id;
-	const char		*label;
-	struct pwm_ops		*ops;
+	struct device		*dev;
+	struct list_head	list;
+	const struct pwm_ops	*ops;
+	int			base;
+	unsigned int		npwm;
+
+	struct pwm_device	*pwms;
 };
 
+int pwm_set_chip_data(struct pwm_device *pwm, void *data);
+void *pwm_get_chip_data(struct pwm_device *pwm);
+
 int pwmchip_add(struct pwm_chip *chip);
 int pwmchip_remove(struct pwm_chip *chip);
+struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
+					 unsigned int index,
+					 const char *label);
 #endif
 
 #endif /* __LINUX_PWM_H */
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 03/17] pwm: Add debugfs interface
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

This commit adds a debugfs interface that can be used to list the
current internal state of the PWM devices registered with the PWM
framework.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
Reviewed-by: Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
Reviewed-by: Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
Changes in v5:
- new patch splitting off debugfs interface implementation
- use seq_file's iterator interface for debugfs support
- add missing forward declaration of struct seq_file

 drivers/pwm/core.c  |   90 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pwm.h |    6 ++++
 2 files changed, 96 insertions(+)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 8688754..7e13da1 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -27,6 +27,8 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 
 #define MAX_PWMS 1024
 
@@ -334,3 +336,91 @@ void pwm_disable(struct pwm_device *pwm)
 		pwm->chip->ops->disable(pwm->chip, pwm);
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
+
+#ifdef CONFIG_DEBUG_FS
+static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
+{
+	unsigned int i;
+
+	for (i = 0; i < chip->npwm; i++) {
+		struct pwm_device *pwm = &chip->pwms[i];
+
+		seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label);
+
+		if (test_bit(PWMF_REQUESTED, &pwm->flags))
+			seq_printf(s, " requested");
+
+		if (test_bit(PWMF_ENABLED, &pwm->flags))
+			seq_printf(s, " enabled");
+
+		seq_printf(s, "\n");
+	}
+}
+
+static void *pwm_seq_start(struct seq_file *s, loff_t *pos)
+{
+	mutex_lock(&pwm_lock);
+	s->private = "";
+
+	return seq_list_start(&pwm_chips, *pos);
+}
+
+static void *pwm_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	s->private = "\n";
+
+	return seq_list_next(v, &pwm_chips, pos);
+}
+
+static void pwm_seq_stop(struct seq_file *s, void *v)
+{
+	mutex_unlock(&pwm_lock);
+}
+
+static int pwm_seq_show(struct seq_file *s, void *v)
+{
+	struct pwm_chip *chip = list_entry(v, struct pwm_chip, list);
+
+	seq_printf(s, "%s%s/%s, %d PWM device%s\n", (char *)s->private,
+		   chip->dev->bus ? chip->dev->bus->name : "no-bus",
+		   dev_name(chip->dev), chip->npwm,
+		   (chip->npwm != 1) ? "s" : "");
+
+	if (chip->ops->dbg_show)
+		chip->ops->dbg_show(chip, s);
+	else
+		pwm_dbg_show(chip, s);
+
+	return 0;
+}
+
+static const struct seq_operations pwm_seq_ops = {
+	.start = pwm_seq_start,
+	.next = pwm_seq_next,
+	.stop = pwm_seq_stop,
+	.show = pwm_seq_show,
+};
+
+static int pwm_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &pwm_seq_ops);
+}
+
+static const struct file_operations pwm_debugfs_ops = {
+	.owner = THIS_MODULE,
+	.open = pwm_seq_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+static int __init pwm_debugfs_init(void)
+{
+	debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL,
+			    &pwm_debugfs_ops);
+
+	return 0;
+}
+
+subsys_initcall(pwm_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 5710391..047cd53 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -2,6 +2,7 @@
 #define __LINUX_PWM_H
 
 struct pwm_device;
+struct seq_file;
 
 /*
  * pwm_request - request a PWM device
@@ -65,6 +66,7 @@ static inline unsigned int pwm_get_period(struct pwm_device *pwm)
  * @config: configure duty cycles and period length for this PWM
  * @enable: enable PWM output toggling
  * @disable: disable PWM output toggling
+ * @dbg_show: optional routine to show contents in debugfs
  * @owner: helps prevent removal of modules exporting active PWMs
  */
 struct pwm_ops {
@@ -79,6 +81,10 @@ struct pwm_ops {
 					  struct pwm_device *pwm);
 	void			(*disable)(struct pwm_chip *chip,
 					   struct pwm_device *pwm);
+#ifdef CONFIG_DEBUG_FS
+	void			(*dbg_show)(struct pwm_chip *chip,
+					    struct seq_file *s);
+#endif
 	struct module		*owner;
 };
 
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 03/17] pwm: Add debugfs interface
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

This commit adds a debugfs interface that can be used to list the
current internal state of the PWM devices registered with the PWM
framework.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Reviewed-by: Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
---
Changes in v5:
- new patch splitting off debugfs interface implementation
- use seq_file's iterator interface for debugfs support
- add missing forward declaration of struct seq_file

 drivers/pwm/core.c  |   90 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pwm.h |    6 ++++
 2 files changed, 96 insertions(+)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 8688754..7e13da1 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -27,6 +27,8 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 
 #define MAX_PWMS 1024
 
@@ -334,3 +336,91 @@ void pwm_disable(struct pwm_device *pwm)
 		pwm->chip->ops->disable(pwm->chip, pwm);
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
+
+#ifdef CONFIG_DEBUG_FS
+static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
+{
+	unsigned int i;
+
+	for (i = 0; i < chip->npwm; i++) {
+		struct pwm_device *pwm = &chip->pwms[i];
+
+		seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label);
+
+		if (test_bit(PWMF_REQUESTED, &pwm->flags))
+			seq_printf(s, " requested");
+
+		if (test_bit(PWMF_ENABLED, &pwm->flags))
+			seq_printf(s, " enabled");
+
+		seq_printf(s, "\n");
+	}
+}
+
+static void *pwm_seq_start(struct seq_file *s, loff_t *pos)
+{
+	mutex_lock(&pwm_lock);
+	s->private = "";
+
+	return seq_list_start(&pwm_chips, *pos);
+}
+
+static void *pwm_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	s->private = "\n";
+
+	return seq_list_next(v, &pwm_chips, pos);
+}
+
+static void pwm_seq_stop(struct seq_file *s, void *v)
+{
+	mutex_unlock(&pwm_lock);
+}
+
+static int pwm_seq_show(struct seq_file *s, void *v)
+{
+	struct pwm_chip *chip = list_entry(v, struct pwm_chip, list);
+
+	seq_printf(s, "%s%s/%s, %d PWM device%s\n", (char *)s->private,
+		   chip->dev->bus ? chip->dev->bus->name : "no-bus",
+		   dev_name(chip->dev), chip->npwm,
+		   (chip->npwm != 1) ? "s" : "");
+
+	if (chip->ops->dbg_show)
+		chip->ops->dbg_show(chip, s);
+	else
+		pwm_dbg_show(chip, s);
+
+	return 0;
+}
+
+static const struct seq_operations pwm_seq_ops = {
+	.start = pwm_seq_start,
+	.next = pwm_seq_next,
+	.stop = pwm_seq_stop,
+	.show = pwm_seq_show,
+};
+
+static int pwm_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &pwm_seq_ops);
+}
+
+static const struct file_operations pwm_debugfs_ops = {
+	.owner = THIS_MODULE,
+	.open = pwm_seq_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+static int __init pwm_debugfs_init(void)
+{
+	debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL,
+			    &pwm_debugfs_ops);
+
+	return 0;
+}
+
+subsys_initcall(pwm_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 5710391..047cd53 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -2,6 +2,7 @@
 #define __LINUX_PWM_H
 
 struct pwm_device;
+struct seq_file;
 
 /*
  * pwm_request - request a PWM device
@@ -65,6 +66,7 @@ static inline unsigned int pwm_get_period(struct pwm_device *pwm)
  * @config: configure duty cycles and period length for this PWM
  * @enable: enable PWM output toggling
  * @disable: disable PWM output toggling
+ * @dbg_show: optional routine to show contents in debugfs
  * @owner: helps prevent removal of modules exporting active PWMs
  */
 struct pwm_ops {
@@ -79,6 +81,10 @@ struct pwm_ops {
 					  struct pwm_device *pwm);
 	void			(*disable)(struct pwm_chip *chip,
 					   struct pwm_device *pwm);
+#ifdef CONFIG_DEBUG_FS
+	void			(*dbg_show)(struct pwm_chip *chip,
+					    struct seq_file *s);
+#endif
 	struct module		*owner;
 };
 
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 04/17] pwm: Add table-based lookup for static mappings
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

In order to get rid of the global namespace for PWM devices, this commit
provides an alternative method, similar to that of the regulator or
clock frameworks, for registering a static mapping for PWM devices. This
works by providing a table with a provider/consumer map in the board
setup code.

With the new pwm_get() and pwm_put() functions available, usage of
pwm_request() and pwm_free() becomes deprecated.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
Changes in v6:
- synchronize documentation with pwm_get() signature
- allow registering multiple PWM devices per user

Changes in v5:
- new patch with table-based lookup for statically registered devices
- update Documentation/pwm.txt

 Documentation/pwm.txt |   27 ++++++--
 drivers/pwm/core.c    |  169 ++++++++++++++++++++++++++++++++++++++++++++-----
 include/linux/pwm.h   |   22 +++++++
 3 files changed, 199 insertions(+), 19 deletions(-)

diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 48f598a..bb3d2f9 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -12,14 +12,33 @@ this kind of flexibility the generic PWM API exists.
 Identifying PWMs
 ----------------
 
-Users of the legacy PWM API use unique IDs to refer to PWM devices. One
-goal of the new PWM framework is to get rid of this global namespace.
+Users of the legacy PWM API use unique IDs to refer to PWM devices.
+
+Instead of referring to a PWM device via its unique ID, board setup code
+should instead register a static mapping that can be used to match PWM
+consumers to providers, as given in the following example:
+
+	static struct pwm_lookup board_pwm_lookup[] = {
+		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),
+	};
+
+	static void __init board_init(void)
+	{
+		...
+		pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup));
+		...
+	}
 
 Using PWMs
 ----------
 
-A PWM can be requested using pwm_request() and freed after usage with
-pwm_free(). After being requested a PWM has to be configured using
+Legacy users can request a PWM device using pwm_request() and free it
+after usage with pwm_free().
+
+New users should use the pwm_get() function and pass to it the consumer
+device or a consumer name. pwm_put() is used to free the PWM device.
+
+After being requested a PWM has to be configured using:
 
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
 
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 7e13da1..402e466 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -32,6 +32,8 @@
 
 #define MAX_PWMS 1024
 
+static DEFINE_MUTEX(pwm_lookup_lock);
+static LIST_HEAD(pwm_lookup_list);
 static DEFINE_MUTEX(pwm_lock);
 static LIST_HEAD(pwm_chips);
 static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
@@ -80,6 +82,29 @@ static void free_pwms(struct pwm_chip *chip)
 	chip->pwms = NULL;
 }
 
+static struct pwm_chip *pwmchip_find_by_name(const char *name)
+{
+	struct pwm_chip *chip;
+
+	if (!name)
+		return NULL;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(chip, &pwm_chips, list) {
+		const char *chip_name = dev_name(chip->dev);
+
+		if (chip_name && strcmp(chip_name, name) == 0) {
+			mutex_unlock(&pwm_lock);
+			return chip;
+		}
+	}
+
+	mutex_unlock(&pwm_lock);
+
+	return NULL;
+}
+
 static int pwm_device_request(struct pwm_device *pwm, const char *label)
 {
 	int err;
@@ -214,6 +239,8 @@ EXPORT_SYMBOL_GPL(pwmchip_remove);
  * pwm_request() - request a PWM device
  * @pwm_id: global PWM device index
  * @label: PWM device label
+ *
+ * This function is deprecated, use pwm_get() instead.
  */
 struct pwm_device *pwm_request(int pwm, const char *label)
 {
@@ -277,24 +304,12 @@ EXPORT_SYMBOL_GPL(pwm_request_from_chip);
 /**
  * pwm_free() - free a PWM device
  * @pwm: PWM device
+ *
+ * This function is deprecated, use pwm_put() instead.
  */
 void pwm_free(struct pwm_device *pwm)
 {
-	mutex_lock(&pwm_lock);
-
-	if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
-		pr_warning("PWM device already freed\n");
-		goto out;
-	}
-
-	if (pwm->chip->ops->free)
-		pwm->chip->ops->free(pwm->chip, pwm);
-
-	pwm->label = NULL;
-
-	module_put(pwm->chip->ops->owner);
-out:
-	mutex_unlock(&pwm_lock);
+	pwm_put(pwm);
 }
 EXPORT_SYMBOL_GPL(pwm_free);
 
@@ -337,6 +352,130 @@ void pwm_disable(struct pwm_device *pwm)
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
 
+/**
+ * pwm_add_table() - register PWM device consumers
+ * @table: array of consumers to register
+ * @num: number of consumers in table
+ */
+void __init pwm_add_table(struct pwm_lookup *table, size_t num)
+{
+	mutex_lock(&pwm_lookup_lock);
+
+	while (num--) {
+		list_add_tail(&table->list, &pwm_lookup_list);
+		table++;
+	}
+
+	mutex_unlock(&pwm_lookup_lock);
+}
+
+/**
+ * pwm_get() - look up and request a PWM device
+ * @dev: device for PWM consumer
+ * @con_id: consumer name
+ *
+ * Look up a PWM chip and a relative index via a table supplied by board setup
+ * code (see pwm_add_table()).
+ *
+ * Once a PWM chip has been found the specified PWM device will be requested
+ * and is ready to be used.
+ */
+struct pwm_device *pwm_get(struct device *dev, const char *con_id)
+{
+	struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
+	const char *dev_id = dev ? dev_name(dev): NULL;
+	struct pwm_chip *chip = NULL;
+	unsigned int best = 0;
+	struct pwm_lookup *p;
+	unsigned int index;
+	unsigned int match;
+
+	/*
+	 * We look up the provider in the static table typically provided by
+	 * board setup code. We first try to lookup the consumer device by
+	 * name. If the consumer device was passed in as NULL or if no match
+	 * was found, we try to find the consumer by directly looking it up
+	 * by name.
+	 *
+	 * If a match is found, the provider PWM chip is looked up by name
+	 * and a PWM device is requested using the PWM device per-chip index.
+	 *
+	 * The lookup algorithm was shamelessly taken from the clock
+	 * framework:
+	 *
+	 * We do slightly fuzzy matching here:
+	 *  An entry with a NULL ID is assumed to be a wildcard.
+	 *  If an entry has a device ID, it must match
+	 *  If an entry has a connection ID, it must match
+	 * Then we take the most specific entry - with the following order
+	 * of precedence: dev+con > dev only > con only.
+	 */
+	mutex_lock(&pwm_lookup_lock);
+
+	list_for_each_entry(p, &pwm_lookup_list, list) {
+		match = 0;
+
+		if (p->dev_id) {
+			if (!dev_id || strcmp(p->dev_id, dev_id))
+				continue;
+
+			match += 2;
+		}
+
+		if (p->con_id) {
+			if (!con_id || strcmp(p->con_id, con_id))
+				continue;
+
+			match += 1;
+		}
+
+		if (match > best) {
+			chip = pwmchip_find_by_name(p->provider);
+			index = p->index;
+
+			if (match != 3)
+				best = match;
+			else
+				break;
+		}
+	}
+
+	if (chip)
+		pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id);
+
+	mutex_unlock(&pwm_lookup_lock);
+
+	return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_get);
+
+/**
+ * pwm_put() - release a PWM device
+ * @pwm: PWM device
+ */
+void pwm_put(struct pwm_device *pwm)
+{
+	if (!pwm)
+		return;
+
+	mutex_lock(&pwm_lock);
+
+	if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
+		pr_warning("PWM device already freed\n");
+		goto out;
+	}
+
+	if (pwm->chip->ops->free)
+		pwm->chip->ops->free(pwm->chip, pwm);
+
+	pwm->label = NULL;
+
+	module_put(pwm->chip->ops->owner);
+out:
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL_GPL(pwm_put);
+
 #ifdef CONFIG_DEBUG_FS
 static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
 {
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 047cd53..2947a4f 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -115,6 +115,28 @@ int pwmchip_remove(struct pwm_chip *chip);
 struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
 					 unsigned int index,
 					 const char *label);
+
+struct pwm_device *pwm_get(struct device *dev, const char *consumer);
+void pwm_put(struct pwm_device *pwm);
+
+struct pwm_lookup {
+	struct list_head list;
+	const char *provider;
+	unsigned int index;
+	const char *dev_id;
+	const char *con_id;
+};
+
+#define PWM_LOOKUP(_provider, _index, _dev_id, _con_id)	\
+	{						\
+		.provider = _provider,			\
+		.index = _index,			\
+		.dev_id = _dev_id,			\
+		.con_id = _con_id,			\
+	}
+
+void pwm_add_table(struct pwm_lookup *table, size_t num);
+
 #endif
 
 #endif /* __LINUX_PWM_H */
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 04/17] pwm: Add table-based lookup for static mappings
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

In order to get rid of the global namespace for PWM devices, this commit
provides an alternative method, similar to that of the regulator or
clock frameworks, for registering a static mapping for PWM devices. This
works by providing a table with a provider/consumer map in the board
setup code.

With the new pwm_get() and pwm_put() functions available, usage of
pwm_request() and pwm_free() becomes deprecated.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
Changes in v6:
- synchronize documentation with pwm_get() signature
- allow registering multiple PWM devices per user

Changes in v5:
- new patch with table-based lookup for statically registered devices
- update Documentation/pwm.txt

 Documentation/pwm.txt |   27 ++++++--
 drivers/pwm/core.c    |  169 ++++++++++++++++++++++++++++++++++++++++++++-----
 include/linux/pwm.h   |   22 +++++++
 3 files changed, 199 insertions(+), 19 deletions(-)

diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 48f598a..bb3d2f9 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -12,14 +12,33 @@ this kind of flexibility the generic PWM API exists.
 Identifying PWMs
 ----------------
 
-Users of the legacy PWM API use unique IDs to refer to PWM devices. One
-goal of the new PWM framework is to get rid of this global namespace.
+Users of the legacy PWM API use unique IDs to refer to PWM devices.
+
+Instead of referring to a PWM device via its unique ID, board setup code
+should instead register a static mapping that can be used to match PWM
+consumers to providers, as given in the following example:
+
+	static struct pwm_lookup board_pwm_lookup[] = {
+		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),
+	};
+
+	static void __init board_init(void)
+	{
+		...
+		pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup));
+		...
+	}
 
 Using PWMs
 ----------
 
-A PWM can be requested using pwm_request() and freed after usage with
-pwm_free(). After being requested a PWM has to be configured using
+Legacy users can request a PWM device using pwm_request() and free it
+after usage with pwm_free().
+
+New users should use the pwm_get() function and pass to it the consumer
+device or a consumer name. pwm_put() is used to free the PWM device.
+
+After being requested a PWM has to be configured using:
 
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
 
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 7e13da1..402e466 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -32,6 +32,8 @@
 
 #define MAX_PWMS 1024
 
+static DEFINE_MUTEX(pwm_lookup_lock);
+static LIST_HEAD(pwm_lookup_list);
 static DEFINE_MUTEX(pwm_lock);
 static LIST_HEAD(pwm_chips);
 static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
@@ -80,6 +82,29 @@ static void free_pwms(struct pwm_chip *chip)
 	chip->pwms = NULL;
 }
 
+static struct pwm_chip *pwmchip_find_by_name(const char *name)
+{
+	struct pwm_chip *chip;
+
+	if (!name)
+		return NULL;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(chip, &pwm_chips, list) {
+		const char *chip_name = dev_name(chip->dev);
+
+		if (chip_name && strcmp(chip_name, name) == 0) {
+			mutex_unlock(&pwm_lock);
+			return chip;
+		}
+	}
+
+	mutex_unlock(&pwm_lock);
+
+	return NULL;
+}
+
 static int pwm_device_request(struct pwm_device *pwm, const char *label)
 {
 	int err;
@@ -214,6 +239,8 @@ EXPORT_SYMBOL_GPL(pwmchip_remove);
  * pwm_request() - request a PWM device
  * @pwm_id: global PWM device index
  * @label: PWM device label
+ *
+ * This function is deprecated, use pwm_get() instead.
  */
 struct pwm_device *pwm_request(int pwm, const char *label)
 {
@@ -277,24 +304,12 @@ EXPORT_SYMBOL_GPL(pwm_request_from_chip);
 /**
  * pwm_free() - free a PWM device
  * @pwm: PWM device
+ *
+ * This function is deprecated, use pwm_put() instead.
  */
 void pwm_free(struct pwm_device *pwm)
 {
-	mutex_lock(&pwm_lock);
-
-	if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
-		pr_warning("PWM device already freed\n");
-		goto out;
-	}
-
-	if (pwm->chip->ops->free)
-		pwm->chip->ops->free(pwm->chip, pwm);
-
-	pwm->label = NULL;
-
-	module_put(pwm->chip->ops->owner);
-out:
-	mutex_unlock(&pwm_lock);
+	pwm_put(pwm);
 }
 EXPORT_SYMBOL_GPL(pwm_free);
 
@@ -337,6 +352,130 @@ void pwm_disable(struct pwm_device *pwm)
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
 
+/**
+ * pwm_add_table() - register PWM device consumers
+ * @table: array of consumers to register
+ * @num: number of consumers in table
+ */
+void __init pwm_add_table(struct pwm_lookup *table, size_t num)
+{
+	mutex_lock(&pwm_lookup_lock);
+
+	while (num--) {
+		list_add_tail(&table->list, &pwm_lookup_list);
+		table++;
+	}
+
+	mutex_unlock(&pwm_lookup_lock);
+}
+
+/**
+ * pwm_get() - look up and request a PWM device
+ * @dev: device for PWM consumer
+ * @con_id: consumer name
+ *
+ * Look up a PWM chip and a relative index via a table supplied by board setup
+ * code (see pwm_add_table()).
+ *
+ * Once a PWM chip has been found the specified PWM device will be requested
+ * and is ready to be used.
+ */
+struct pwm_device *pwm_get(struct device *dev, const char *con_id)
+{
+	struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
+	const char *dev_id = dev ? dev_name(dev): NULL;
+	struct pwm_chip *chip = NULL;
+	unsigned int best = 0;
+	struct pwm_lookup *p;
+	unsigned int index;
+	unsigned int match;
+
+	/*
+	 * We look up the provider in the static table typically provided by
+	 * board setup code. We first try to lookup the consumer device by
+	 * name. If the consumer device was passed in as NULL or if no match
+	 * was found, we try to find the consumer by directly looking it up
+	 * by name.
+	 *
+	 * If a match is found, the provider PWM chip is looked up by name
+	 * and a PWM device is requested using the PWM device per-chip index.
+	 *
+	 * The lookup algorithm was shamelessly taken from the clock
+	 * framework:
+	 *
+	 * We do slightly fuzzy matching here:
+	 *  An entry with a NULL ID is assumed to be a wildcard.
+	 *  If an entry has a device ID, it must match
+	 *  If an entry has a connection ID, it must match
+	 * Then we take the most specific entry - with the following order
+	 * of precedence: dev+con > dev only > con only.
+	 */
+	mutex_lock(&pwm_lookup_lock);
+
+	list_for_each_entry(p, &pwm_lookup_list, list) {
+		match = 0;
+
+		if (p->dev_id) {
+			if (!dev_id || strcmp(p->dev_id, dev_id))
+				continue;
+
+			match += 2;
+		}
+
+		if (p->con_id) {
+			if (!con_id || strcmp(p->con_id, con_id))
+				continue;
+
+			match += 1;
+		}
+
+		if (match > best) {
+			chip = pwmchip_find_by_name(p->provider);
+			index = p->index;
+
+			if (match != 3)
+				best = match;
+			else
+				break;
+		}
+	}
+
+	if (chip)
+		pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id);
+
+	mutex_unlock(&pwm_lookup_lock);
+
+	return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_get);
+
+/**
+ * pwm_put() - release a PWM device
+ * @pwm: PWM device
+ */
+void pwm_put(struct pwm_device *pwm)
+{
+	if (!pwm)
+		return;
+
+	mutex_lock(&pwm_lock);
+
+	if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
+		pr_warning("PWM device already freed\n");
+		goto out;
+	}
+
+	if (pwm->chip->ops->free)
+		pwm->chip->ops->free(pwm->chip, pwm);
+
+	pwm->label = NULL;
+
+	module_put(pwm->chip->ops->owner);
+out:
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL_GPL(pwm_put);
+
 #ifdef CONFIG_DEBUG_FS
 static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
 {
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 047cd53..2947a4f 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -115,6 +115,28 @@ int pwmchip_remove(struct pwm_chip *chip);
 struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
 					 unsigned int index,
 					 const char *label);
+
+struct pwm_device *pwm_get(struct device *dev, const char *consumer);
+void pwm_put(struct pwm_device *pwm);
+
+struct pwm_lookup {
+	struct list_head list;
+	const char *provider;
+	unsigned int index;
+	const char *dev_id;
+	const char *con_id;
+};
+
+#define PWM_LOOKUP(_provider, _index, _dev_id, _con_id)	\
+	{						\
+		.provider = _provider,			\
+		.index = _index,			\
+		.dev_id = _dev_id,			\
+		.con_id = _con_id,			\
+	}
+
+void pwm_add_table(struct pwm_lookup *table, size_t num);
+
 #endif
 
 #endif /* __LINUX_PWM_H */
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 05/17] pwm: Add device tree support
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

This patch adds helpers to support device tree bindings for the generic
PWM API. Device tree binding documentation for PWM controllers is also
provided.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
---
Changes in v6:
- don't override error code in of_pwm_simple_xlate()
- no longer export of_pwm_request(), only pwm_get() should be used
- do a reverse lookup of the index into the "pwms" property by matching
  the con_id parameter to the "pwm-names" property

Changes in v5:
- use IS_ENABLED(CONFIG_OF) along with the compiler's DCE instead of
  empty implementations of of_pwmchip_add() and of_pwmchip_remove() in
  the !OF case
- remove OF_PWM Kconfig symbol and make OF support code conditional on
  the OF symbol only
- use "pwms" and "pwm-names" fixed property names
- remove redundant check in of_pwm_simple_xlate()
- use pwm_request_from_chip() instead of pwm_request_from_device()
- integrate OF support with pwm_get() from new patch 4

Changes in v4:
- add OF-specific code removed from patch 2
- make of_node_to_pwmchip() and of_pwm_simple_xlate() static
- rename of_get_named_pwm() to of_pwm_request(), return a struct
  pwm_device, get rid of the now unused spec parameter and use the
  device_node.name field as the PWM's name
- return a requested struct pwm_device from pwm_chip.of_xlate and
  drop the now unused spec parameter
- move OF support code into drivers/pwm/core.c
- used deferred probing if a PWM chip is not available yet

Changes in v2:
- add device tree binding documentation
- add of_xlate to parse controller-specific PWM-specifier

 Documentation/devicetree/bindings/pwm/pwm.txt |   57 ++++++++++
 drivers/pwm/core.c                            |  151 ++++++++++++++++++++++++-
 include/linux/pwm.h                           |    6 +
 3 files changed, 212 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm.txt

diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
new file mode 100644
index 0000000..73ec962
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm.txt
@@ -0,0 +1,57 @@
+Specifying PWM information for devices
+======================================
+
+1) PWM user nodes
+-----------------
+
+PWM users should specify a list of PWM devices that they want to use
+with a property containing a 'pwm-list':
+
+	pwm-list ::= <single-pwm> [pwm-list]
+	single-pwm ::= <pwm-phandle> <pwm-specifier>
+	pwm-phandle : phandle to PWM controller node
+	pwm-specifier : array of #pwm-cells specifying the given PWM
+			(controller specific)
+
+PWM properties should be named "pwms". The exact meaning of each pwms
+property must be documented in the device tree binding for each device.
+An optional property "pwm-names" may contain a list of strings to label
+each of the PWM devices listed in the "pwms" property. If no "pwm-names"
+property is given, the name of the user node will be used as fallback.
+
+Drivers for devices that use more than a single PWM device can use the
+"pwm-names" property to map the name of the PWM device requested by the
+pwm_get() call to an index into the list given by the "pwms" property.
+
+The following example could be used to describe a PWM-based backlight
+device:
+
+	pwm: pwm {
+		#pwm-cells = <2>;
+	};
+
+	[...]
+
+	bl: backlight {
+		pwms = <&pwm 0 5000000>;
+		pwm-names = "backlight";
+	};
+
+pwm-specifier typically encodes the chip-relative PWM number and the PWM
+period in nanoseconds. Note that in the example above, specifying the
+"pwm-names" is redundant because the name "backlight" would be used as
+fallback anyway.
+
+2) PWM controller nodes
+-----------------------
+
+PWM controller nodes must specify the number of cells used for the
+specifier using the '#pwm-cells' property.
+
+An example PWM controller might look like this:
+
+	pwm: pwm@7000a000 {
+		compatible = "nvidia,tegra20-pwm";
+		reg = <0x7000a000 0x100>;
+		#pwm-cells = <2>;
+	};
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 402e466..e5ab7cd 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -129,6 +129,45 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
 	return 0;
 }
 
+static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc,
+					      const struct of_phandle_args *args)
+{
+	struct pwm_device *pwm;
+
+	if (pc->of_pwm_n_cells < 2)
+		return ERR_PTR(-EINVAL);
+
+	if (args->args[0] >= pc->npwm)
+		return ERR_PTR(-EINVAL);
+
+	pwm = pwm_request_from_chip(pc, args->args[0], NULL);
+	if (IS_ERR(pwm))
+		return pwm;
+
+	pwm_set_period(pwm, args->args[1]);
+
+	return pwm;
+}
+
+void of_pwmchip_add(struct pwm_chip *chip)
+{
+	if (!chip->dev || !chip->dev->of_node)
+		return;
+
+	if (!chip->of_xlate) {
+		chip->of_xlate = of_pwm_simple_xlate;
+		chip->of_pwm_n_cells = 2;
+	}
+
+	of_node_get(chip->dev->of_node);
+}
+
+void of_pwmchip_remove(struct pwm_chip *chip)
+{
+	if (chip->dev && chip->dev->of_node)
+		of_node_put(chip->dev->of_node);
+}
+
 /**
  * pwm_set_chip_data() - set private chip data for a PWM
  * @pwm: PWM device
@@ -197,6 +236,9 @@ int pwmchip_add(struct pwm_chip *chip)
 	INIT_LIST_HEAD(&chip->list);
 	list_add(&chip->list, &pwm_chips);
 
+	if (IS_ENABLED(CONFIG_OF))
+		of_pwmchip_add(chip);
+
 out:
 	mutex_unlock(&pwm_lock);
 	return ret;
@@ -227,6 +269,10 @@ int pwmchip_remove(struct pwm_chip *chip)
 	}
 
 	list_del_init(&chip->list);
+
+	if (IS_ENABLED(CONFIG_OF))
+		of_pwmchip_remove(chip);
+
 	free_pwms(chip);
 
 out:
@@ -352,6 +398,102 @@ void pwm_disable(struct pwm_device *pwm)
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
 
+#ifdef CONFIG_OF
+static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
+{
+	struct pwm_chip *chip;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(chip, &pwm_chips, list)
+		if (chip->dev && chip->dev->of_node == np) {
+			mutex_unlock(&pwm_lock);
+			return chip;
+		}
+
+	mutex_unlock(&pwm_lock);
+
+	return ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * of_pwm_request() - request a PWM via the PWM framework
+ * @np: device node to get the PWM from
+ * @con_id: consumer name
+ *
+ * Returns the PWM device parsed from the phandle and index specified in the
+ * "pwms" property of a device tree node or a negative error-code on failure.
+ * Values parsed from the device tree are stored in the returned PWM device
+ * object.
+ *
+ * If con_id is NULL, the first PWM device listed in the "pwms" property will
+ * be requested. Otherwise the "pwm-names" property is used to do a reverse
+ * lookup of the PWM index. This also means that the "pwm-names" property
+ * becomes mandatory for devices that look up the PWM device via the con_id
+ * parameter.
+ */
+static struct pwm_device *of_pwm_request(struct device_node *np,
+					 const char *con_id)
+{
+	struct pwm_device *pwm = NULL;
+	struct of_phandle_args args;
+	struct pwm_chip *pc;
+	int index = 0;
+	int err;
+
+	if (con_id) {
+		index = of_property_match_string(np, "pwm-names", con_id);
+		if (index < 0)
+			return ERR_PTR(index);
+	}
+
+	err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
+					 &args);
+	if (err) {
+		pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
+		return ERR_PTR(err);
+	}
+
+	pc = of_node_to_pwmchip(args.np);
+	if (IS_ERR(pc)) {
+		pr_debug("%s(): PWM chip not found\n", __func__);
+		pwm = ERR_CAST(pc);
+		goto put;
+	}
+
+	if (args.args_count != pc->of_pwm_n_cells) {
+		pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
+			 args.np->full_name);
+		pwm = ERR_PTR(-EINVAL);
+		goto put;
+	}
+
+	pwm = pc->of_xlate(pc, &args);
+	if (IS_ERR(pwm))
+		goto put;
+
+	/*
+	 * If a consumer name was not given, try to look it up from the
+	 * "pwm-names" property if it exists. Otherwise use the name of
+	 * the user device node.
+	 */
+	if (!con_id) {
+		err = of_property_read_string_index(np, "pwm-names", index,
+						    &con_id);
+		if (err < 0)
+			con_id = np->name;
+	}
+
+	pwm->label = con_id;
+
+put:
+	of_node_put(args.np);
+
+	return pwm;
+}
+EXPORT_SYMBOL(of_pwm_request);
+#endif
+
 /**
  * pwm_add_table() - register PWM device consumers
  * @table: array of consumers to register
@@ -374,8 +516,9 @@ void __init pwm_add_table(struct pwm_lookup *table, size_t num)
  * @dev: device for PWM consumer
  * @con_id: consumer name
  *
- * Look up a PWM chip and a relative index via a table supplied by board setup
- * code (see pwm_add_table()).
+ * Lookup is first attempted using DT. If the device was not instantiated from
+ * a device tree, a PWM chip and a relative index is looked up via a table
+ * supplied by board setup code (see pwm_add_table()).
  *
  * Once a PWM chip has been found the specified PWM device will be requested
  * and is ready to be used.
@@ -390,6 +533,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
 	unsigned int index;
 	unsigned int match;
 
+	/* look up via DT first */
+	if (dev && dev->of_node)
+		return of_pwm_request(dev->of_node, con_id);
+
 	/*
 	 * We look up the provider in the static table typically provided by
 	 * board setup code. We first try to lookup the consumer device by
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 2947a4f..21d076c 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -1,6 +1,8 @@
 #ifndef __LINUX_PWM_H
 #define __LINUX_PWM_H
 
+#include <linux/of.h>
+
 struct pwm_device;
 struct seq_file;
 
@@ -105,6 +107,10 @@ struct pwm_chip {
 	unsigned int		npwm;
 
 	struct pwm_device	*pwms;
+
+	struct pwm_device *	(*of_xlate)(struct pwm_chip *pc,
+					    const struct of_phandle_args *args);
+	unsigned int		of_pwm_n_cells;
 };
 
 int pwm_set_chip_data(struct pwm_device *pwm, void *data);
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 05/17] pwm: Add device tree support
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds helpers to support device tree bindings for the generic
PWM API. Device tree binding documentation for PWM controllers is also
provided.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
Changes in v6:
- don't override error code in of_pwm_simple_xlate()
- no longer export of_pwm_request(), only pwm_get() should be used
- do a reverse lookup of the index into the "pwms" property by matching
  the con_id parameter to the "pwm-names" property

Changes in v5:
- use IS_ENABLED(CONFIG_OF) along with the compiler's DCE instead of
  empty implementations of of_pwmchip_add() and of_pwmchip_remove() in
  the !OF case
- remove OF_PWM Kconfig symbol and make OF support code conditional on
  the OF symbol only
- use "pwms" and "pwm-names" fixed property names
- remove redundant check in of_pwm_simple_xlate()
- use pwm_request_from_chip() instead of pwm_request_from_device()
- integrate OF support with pwm_get() from new patch 4

Changes in v4:
- add OF-specific code removed from patch 2
- make of_node_to_pwmchip() and of_pwm_simple_xlate() static
- rename of_get_named_pwm() to of_pwm_request(), return a struct
  pwm_device, get rid of the now unused spec parameter and use the
  device_node.name field as the PWM's name
- return a requested struct pwm_device from pwm_chip.of_xlate and
  drop the now unused spec parameter
- move OF support code into drivers/pwm/core.c
- used deferred probing if a PWM chip is not available yet

Changes in v2:
- add device tree binding documentation
- add of_xlate to parse controller-specific PWM-specifier

 Documentation/devicetree/bindings/pwm/pwm.txt |   57 ++++++++++
 drivers/pwm/core.c                            |  151 ++++++++++++++++++++++++-
 include/linux/pwm.h                           |    6 +
 3 files changed, 212 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm.txt

diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
new file mode 100644
index 0000000..73ec962
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm.txt
@@ -0,0 +1,57 @@
+Specifying PWM information for devices
+======================================
+
+1) PWM user nodes
+-----------------
+
+PWM users should specify a list of PWM devices that they want to use
+with a property containing a 'pwm-list':
+
+	pwm-list ::= <single-pwm> [pwm-list]
+	single-pwm ::= <pwm-phandle> <pwm-specifier>
+	pwm-phandle : phandle to PWM controller node
+	pwm-specifier : array of #pwm-cells specifying the given PWM
+			(controller specific)
+
+PWM properties should be named "pwms". The exact meaning of each pwms
+property must be documented in the device tree binding for each device.
+An optional property "pwm-names" may contain a list of strings to label
+each of the PWM devices listed in the "pwms" property. If no "pwm-names"
+property is given, the name of the user node will be used as fallback.
+
+Drivers for devices that use more than a single PWM device can use the
+"pwm-names" property to map the name of the PWM device requested by the
+pwm_get() call to an index into the list given by the "pwms" property.
+
+The following example could be used to describe a PWM-based backlight
+device:
+
+	pwm: pwm {
+		#pwm-cells = <2>;
+	};
+
+	[...]
+
+	bl: backlight {
+		pwms = <&pwm 0 5000000>;
+		pwm-names = "backlight";
+	};
+
+pwm-specifier typically encodes the chip-relative PWM number and the PWM
+period in nanoseconds. Note that in the example above, specifying the
+"pwm-names" is redundant because the name "backlight" would be used as
+fallback anyway.
+
+2) PWM controller nodes
+-----------------------
+
+PWM controller nodes must specify the number of cells used for the
+specifier using the '#pwm-cells' property.
+
+An example PWM controller might look like this:
+
+	pwm: pwm at 7000a000 {
+		compatible = "nvidia,tegra20-pwm";
+		reg = <0x7000a000 0x100>;
+		#pwm-cells = <2>;
+	};
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 402e466..e5ab7cd 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -129,6 +129,45 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
 	return 0;
 }
 
+static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc,
+					      const struct of_phandle_args *args)
+{
+	struct pwm_device *pwm;
+
+	if (pc->of_pwm_n_cells < 2)
+		return ERR_PTR(-EINVAL);
+
+	if (args->args[0] >= pc->npwm)
+		return ERR_PTR(-EINVAL);
+
+	pwm = pwm_request_from_chip(pc, args->args[0], NULL);
+	if (IS_ERR(pwm))
+		return pwm;
+
+	pwm_set_period(pwm, args->args[1]);
+
+	return pwm;
+}
+
+void of_pwmchip_add(struct pwm_chip *chip)
+{
+	if (!chip->dev || !chip->dev->of_node)
+		return;
+
+	if (!chip->of_xlate) {
+		chip->of_xlate = of_pwm_simple_xlate;
+		chip->of_pwm_n_cells = 2;
+	}
+
+	of_node_get(chip->dev->of_node);
+}
+
+void of_pwmchip_remove(struct pwm_chip *chip)
+{
+	if (chip->dev && chip->dev->of_node)
+		of_node_put(chip->dev->of_node);
+}
+
 /**
  * pwm_set_chip_data() - set private chip data for a PWM
  * @pwm: PWM device
@@ -197,6 +236,9 @@ int pwmchip_add(struct pwm_chip *chip)
 	INIT_LIST_HEAD(&chip->list);
 	list_add(&chip->list, &pwm_chips);
 
+	if (IS_ENABLED(CONFIG_OF))
+		of_pwmchip_add(chip);
+
 out:
 	mutex_unlock(&pwm_lock);
 	return ret;
@@ -227,6 +269,10 @@ int pwmchip_remove(struct pwm_chip *chip)
 	}
 
 	list_del_init(&chip->list);
+
+	if (IS_ENABLED(CONFIG_OF))
+		of_pwmchip_remove(chip);
+
 	free_pwms(chip);
 
 out:
@@ -352,6 +398,102 @@ void pwm_disable(struct pwm_device *pwm)
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
 
+#ifdef CONFIG_OF
+static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
+{
+	struct pwm_chip *chip;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(chip, &pwm_chips, list)
+		if (chip->dev && chip->dev->of_node == np) {
+			mutex_unlock(&pwm_lock);
+			return chip;
+		}
+
+	mutex_unlock(&pwm_lock);
+
+	return ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * of_pwm_request() - request a PWM via the PWM framework
+ * @np: device node to get the PWM from
+ * @con_id: consumer name
+ *
+ * Returns the PWM device parsed from the phandle and index specified in the
+ * "pwms" property of a device tree node or a negative error-code on failure.
+ * Values parsed from the device tree are stored in the returned PWM device
+ * object.
+ *
+ * If con_id is NULL, the first PWM device listed in the "pwms" property will
+ * be requested. Otherwise the "pwm-names" property is used to do a reverse
+ * lookup of the PWM index. This also means that the "pwm-names" property
+ * becomes mandatory for devices that look up the PWM device via the con_id
+ * parameter.
+ */
+static struct pwm_device *of_pwm_request(struct device_node *np,
+					 const char *con_id)
+{
+	struct pwm_device *pwm = NULL;
+	struct of_phandle_args args;
+	struct pwm_chip *pc;
+	int index = 0;
+	int err;
+
+	if (con_id) {
+		index = of_property_match_string(np, "pwm-names", con_id);
+		if (index < 0)
+			return ERR_PTR(index);
+	}
+
+	err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
+					 &args);
+	if (err) {
+		pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
+		return ERR_PTR(err);
+	}
+
+	pc = of_node_to_pwmchip(args.np);
+	if (IS_ERR(pc)) {
+		pr_debug("%s(): PWM chip not found\n", __func__);
+		pwm = ERR_CAST(pc);
+		goto put;
+	}
+
+	if (args.args_count != pc->of_pwm_n_cells) {
+		pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
+			 args.np->full_name);
+		pwm = ERR_PTR(-EINVAL);
+		goto put;
+	}
+
+	pwm = pc->of_xlate(pc, &args);
+	if (IS_ERR(pwm))
+		goto put;
+
+	/*
+	 * If a consumer name was not given, try to look it up from the
+	 * "pwm-names" property if it exists. Otherwise use the name of
+	 * the user device node.
+	 */
+	if (!con_id) {
+		err = of_property_read_string_index(np, "pwm-names", index,
+						    &con_id);
+		if (err < 0)
+			con_id = np->name;
+	}
+
+	pwm->label = con_id;
+
+put:
+	of_node_put(args.np);
+
+	return pwm;
+}
+EXPORT_SYMBOL(of_pwm_request);
+#endif
+
 /**
  * pwm_add_table() - register PWM device consumers
  * @table: array of consumers to register
@@ -374,8 +516,9 @@ void __init pwm_add_table(struct pwm_lookup *table, size_t num)
  * @dev: device for PWM consumer
  * @con_id: consumer name
  *
- * Look up a PWM chip and a relative index via a table supplied by board setup
- * code (see pwm_add_table()).
+ * Lookup is first attempted using DT. If the device was not instantiated from
+ * a device tree, a PWM chip and a relative index is looked up via a table
+ * supplied by board setup code (see pwm_add_table()).
  *
  * Once a PWM chip has been found the specified PWM device will be requested
  * and is ready to be used.
@@ -390,6 +533,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
 	unsigned int index;
 	unsigned int match;
 
+	/* look up via DT first */
+	if (dev && dev->of_node)
+		return of_pwm_request(dev->of_node, con_id);
+
 	/*
 	 * We look up the provider in the static table typically provided by
 	 * board setup code. We first try to lookup the consumer device by
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 2947a4f..21d076c 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -1,6 +1,8 @@
 #ifndef __LINUX_PWM_H
 #define __LINUX_PWM_H
 
+#include <linux/of.h>
+
 struct pwm_device;
 struct seq_file;
 
@@ -105,6 +107,10 @@ struct pwm_chip {
 	unsigned int		npwm;
 
 	struct pwm_device	*pwms;
+
+	struct pwm_device *	(*of_xlate)(struct pwm_chip *pc,
+					    const struct of_phandle_args *args);
+	unsigned int		of_pwm_n_cells;
 };
 
 int pwm_set_chip_data(struct pwm_device *pwm, void *data);
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 06/17] ARM: tegra: Fix PWM clock programming
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: Simon Que, Bill Huang,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

From: Simon Que <sque-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>

PWM clock source registers in Tegra 2 have different clock source selection bit
fields than other registers.  PWM clock source bits in CLK_SOURCE_PWM_0 register
are located at bit field bit[30:28] while others are at bit field bit[31:30] in
their respective clock source register.

This patch updates the clock programming to correctly reflect that, by adding a
flag to indicate the alternate bit field format and checking for it when
selecting a clock source (parent clock).

Signed-off-by: Bill Huang <bilhuang-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Simon Que <sque-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
Acked-by: Stephen Warren <swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
---
Changes in v4:
  - reuse PWM flag from Tegra 30 clock programming
  - mask out bit 31 explicitly

 arch/arm/mach-tegra/tegra2_clocks.c |   32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 592a4ee..64735bd 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -69,6 +69,8 @@
 
 #define PERIPH_CLK_SOURCE_MASK		(3<<30)
 #define PERIPH_CLK_SOURCE_SHIFT		30
+#define PERIPH_CLK_SOURCE_PWM_MASK	(7<<28)
+#define PERIPH_CLK_SOURCE_PWM_SHIFT	28
 #define PERIPH_CLK_SOURCE_ENABLE	(1<<28)
 #define PERIPH_CLK_SOURCE_DIVU71_MASK	0xFF
 #define PERIPH_CLK_SOURCE_DIVU16_MASK	0xFFFF
@@ -908,9 +910,20 @@ static void tegra2_periph_clk_init(struct clk *c)
 	u32 val = clk_readl(c->reg);
 	const struct clk_mux_sel *mux = NULL;
 	const struct clk_mux_sel *sel;
+	u32 shift;
+	u32 mask;
+
+	if (c->flags & MUX_PWM) {
+		shift = PERIPH_CLK_SOURCE_PWM_SHIFT;
+		mask = PERIPH_CLK_SOURCE_PWM_MASK;
+	} else {
+		shift = PERIPH_CLK_SOURCE_SHIFT;
+		mask = PERIPH_CLK_SOURCE_MASK;
+	}
+
 	if (c->flags & MUX) {
 		for (sel = c->inputs; sel->input != NULL; sel++) {
-			if (val >> PERIPH_CLK_SOURCE_SHIFT == sel->value)
+			if ((val & mask) >> shift == sel->value)
 				mux = sel;
 		}
 		BUG_ON(!mux);
@@ -1023,12 +1036,23 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
 {
 	u32 val;
 	const struct clk_mux_sel *sel;
+	u32 mask, shift;
+
 	pr_debug("%s: %s %s\n", __func__, c->name, p->name);
+
+	if (c->flags & MUX_PWM) {
+		shift = PERIPH_CLK_SOURCE_PWM_SHIFT;
+		mask = PERIPH_CLK_SOURCE_PWM_MASK;
+	} else {
+		shift = PERIPH_CLK_SOURCE_SHIFT;
+		mask = PERIPH_CLK_SOURCE_MASK;
+	}
+
 	for (sel = c->inputs; sel->input != NULL; sel++) {
 		if (sel->input == p) {
 			val = clk_readl(c->reg);
-			val &= ~PERIPH_CLK_SOURCE_MASK;
-			val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT;
+			val &= ~mask;
+			val |= (sel->value) << shift;
 
 			if (c->refcnt)
 				clk_enable(p);
@@ -2146,7 +2170,7 @@ static struct clk tegra_list_clks[] = {
 	PERIPH_CLK("i2s2",	"tegra-i2s.1",		NULL,	18,	0x104,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_out",	"spdif_out",		NULL,	10,	0x108,	100000000, mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_in",	"spdif_in",		NULL,	10,	0x10c,	100000000, mux_pllp_pllc_pllm,		MUX | DIV_U71),
-	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71),
+	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71 | MUX_PWM),
 	PERIPH_CLK("spi",	"spi",			NULL,	43,	0x114,	40000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("xio",	"xio",			NULL,	45,	0x120,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("twc",	"twc",			NULL,	16,	0x12c,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 06/17] ARM: tegra: Fix PWM clock programming
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

From: Simon Que <sque@chromium.org>

PWM clock source registers in Tegra 2 have different clock source selection bit
fields than other registers.  PWM clock source bits in CLK_SOURCE_PWM_0 register
are located at bit field bit[30:28] while others are at bit field bit[31:30] in
their respective clock source register.

This patch updates the clock programming to correctly reflect that, by adding a
flag to indicate the alternate bit field format and checking for it when
selecting a clock source (parent clock).

Signed-off-by: Bill Huang <bilhuang@nvidia.com>
Signed-off-by: Simon Que <sque@chromium.org>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Stephen Warren <swarren@wwwdotorg.org>
---
Changes in v4:
  - reuse PWM flag from Tegra 30 clock programming
  - mask out bit 31 explicitly

 arch/arm/mach-tegra/tegra2_clocks.c |   32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 592a4ee..64735bd 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -69,6 +69,8 @@
 
 #define PERIPH_CLK_SOURCE_MASK		(3<<30)
 #define PERIPH_CLK_SOURCE_SHIFT		30
+#define PERIPH_CLK_SOURCE_PWM_MASK	(7<<28)
+#define PERIPH_CLK_SOURCE_PWM_SHIFT	28
 #define PERIPH_CLK_SOURCE_ENABLE	(1<<28)
 #define PERIPH_CLK_SOURCE_DIVU71_MASK	0xFF
 #define PERIPH_CLK_SOURCE_DIVU16_MASK	0xFFFF
@@ -908,9 +910,20 @@ static void tegra2_periph_clk_init(struct clk *c)
 	u32 val = clk_readl(c->reg);
 	const struct clk_mux_sel *mux = NULL;
 	const struct clk_mux_sel *sel;
+	u32 shift;
+	u32 mask;
+
+	if (c->flags & MUX_PWM) {
+		shift = PERIPH_CLK_SOURCE_PWM_SHIFT;
+		mask = PERIPH_CLK_SOURCE_PWM_MASK;
+	} else {
+		shift = PERIPH_CLK_SOURCE_SHIFT;
+		mask = PERIPH_CLK_SOURCE_MASK;
+	}
+
 	if (c->flags & MUX) {
 		for (sel = c->inputs; sel->input != NULL; sel++) {
-			if (val >> PERIPH_CLK_SOURCE_SHIFT == sel->value)
+			if ((val & mask) >> shift == sel->value)
 				mux = sel;
 		}
 		BUG_ON(!mux);
@@ -1023,12 +1036,23 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
 {
 	u32 val;
 	const struct clk_mux_sel *sel;
+	u32 mask, shift;
+
 	pr_debug("%s: %s %s\n", __func__, c->name, p->name);
+
+	if (c->flags & MUX_PWM) {
+		shift = PERIPH_CLK_SOURCE_PWM_SHIFT;
+		mask = PERIPH_CLK_SOURCE_PWM_MASK;
+	} else {
+		shift = PERIPH_CLK_SOURCE_SHIFT;
+		mask = PERIPH_CLK_SOURCE_MASK;
+	}
+
 	for (sel = c->inputs; sel->input != NULL; sel++) {
 		if (sel->input == p) {
 			val = clk_readl(c->reg);
-			val &= ~PERIPH_CLK_SOURCE_MASK;
-			val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT;
+			val &= ~mask;
+			val |= (sel->value) << shift;
 
 			if (c->refcnt)
 				clk_enable(p);
@@ -2146,7 +2170,7 @@ static struct clk tegra_list_clks[] = {
 	PERIPH_CLK("i2s2",	"tegra-i2s.1",		NULL,	18,	0x104,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_out",	"spdif_out",		NULL,	10,	0x108,	100000000, mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_in",	"spdif_in",		NULL,	10,	0x10c,	100000000, mux_pllp_pllc_pllm,		MUX | DIV_U71),
-	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71),
+	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71 | MUX_PWM),
 	PERIPH_CLK("spi",	"spi",			NULL,	43,	0x114,	40000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("xio",	"xio",			NULL,	45,	0x120,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("twc",	"twc",			NULL,	16,	0x12c,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 07/17] ARM: tegra: Provide clock for only one PWM controller
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

A subsequent patch will add a generic PWM API driver for the Tegra PWFM
controller, supporting all four PWM devices with a single PWM chip. The
device will be named tegra-pwm and only one clock needs to be provided.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
Acked-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
Changes in v5:
- rename the device name to "tegra-pwm" in the clock lookup table
- get rid of all CLK_DUPLICATE entries which are no longer needed

 arch/arm/mach-tegra/tegra2_clocks.c  |    6 +-----
 arch/arm/mach-tegra/tegra30_clocks.c |    6 +-----
 2 files changed, 2 insertions(+), 10 deletions(-)

diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 64735bd..c95a878 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -2170,7 +2170,7 @@ static struct clk tegra_list_clks[] = {
 	PERIPH_CLK("i2s2",	"tegra-i2s.1",		NULL,	18,	0x104,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_out",	"spdif_out",		NULL,	10,	0x108,	100000000, mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_in",	"spdif_in",		NULL,	10,	0x10c,	100000000, mux_pllp_pllc_pllm,		MUX | DIV_U71),
-	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71 | MUX_PWM),
+	PERIPH_CLK("pwm",	"tegra-pwm",		NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71 | MUX_PWM),
 	PERIPH_CLK("spi",	"spi",			NULL,	43,	0x114,	40000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("xio",	"xio",			NULL,	45,	0x120,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("twc",	"twc",			NULL,	16,	0x12c,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
@@ -2269,10 +2269,6 @@ static struct clk_duplicate tegra_clk_duplicates[] = {
 	CLK_DUPLICATE("usbd", "tegra-otg", NULL),
 	CLK_DUPLICATE("hdmi", "tegradc.0", "hdmi"),
 	CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
-	CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
 	CLK_DUPLICATE("host1x", "tegra_grhost", "host1x"),
 	CLK_DUPLICATE("2d", "tegra_grhost", "gr2d"),
 	CLK_DUPLICATE("3d", "tegra_grhost", "gr3d"),
diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c
index 6d08b53..18afd0c 100644
--- a/arch/arm/mach-tegra/tegra30_clocks.c
+++ b/arch/arm/mach-tegra/tegra30_clocks.c
@@ -2886,7 +2886,7 @@ struct clk tegra_list_clks[] = {
 	PERIPH_CLK("i2s4",	"tegra30-i2s.4",	NULL,	102,	0x3c0,	26000000,  mux_pllaout0_audio4_2x_pllp_clkm,	MUX | DIV_U71 | PERIPH_ON_APB),
 	PERIPH_CLK("spdif_out",	"tegra30-spdif",	"spdif_out",	10,	0x108,	100000000, mux_pllaout0_audio_2x_pllp_clkm,	MUX | DIV_U71 | PERIPH_ON_APB),
 	PERIPH_CLK("spdif_in",	"tegra30-spdif",	"spdif_in",	10,	0x10c,	100000000, mux_pllp_pllc_pllm,		MUX | DIV_U71 | PERIPH_ON_APB),
-	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_clk32_clkm,	MUX | MUX_PWM | DIV_U71 | PERIPH_ON_APB),
+	PERIPH_CLK("pwm",	"tegra-pwm",		NULL,	17,	0x110,	432000000, mux_pllp_pllc_clk32_clkm,	MUX | MUX_PWM | DIV_U71 | PERIPH_ON_APB),
 	PERIPH_CLK("d_audio",	"tegra30-ahub",		"d_audio", 106,	0x3d0,	48000000,  mux_plla_pllc_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("dam0",	"tegra30-dam.0",	NULL,   108,	0x3d8,	48000000,  mux_plla_pllc_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("dam1",	"tegra30-dam.1",	NULL,   109,	0x3dc,	48000000,  mux_plla_pllc_pllp_clkm,	MUX | DIV_U71),
@@ -2990,10 +2990,6 @@ struct clk_duplicate tegra_clk_duplicates[] = {
 	CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
 	CLK_DUPLICATE("dsib", "tegradc.0", "dsib"),
 	CLK_DUPLICATE("dsia", "tegradc.1", "dsia"),
-	CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
 	CLK_DUPLICATE("bsev", "tegra-avp", "bsev"),
 	CLK_DUPLICATE("bsev", "nvavp", "bsev"),
 	CLK_DUPLICATE("vde", "tegra-aes", "vde"),
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 07/17] ARM: tegra: Provide clock for only one PWM controller
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

A subsequent patch will add a generic PWM API driver for the Tegra PWFM
controller, supporting all four PWM devices with a single PWM chip. The
device will be named tegra-pwm and only one clock needs to be provided.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Stephen Warren <swarren@nvidia.com>
---
Changes in v5:
- rename the device name to "tegra-pwm" in the clock lookup table
- get rid of all CLK_DUPLICATE entries which are no longer needed

 arch/arm/mach-tegra/tegra2_clocks.c  |    6 +-----
 arch/arm/mach-tegra/tegra30_clocks.c |    6 +-----
 2 files changed, 2 insertions(+), 10 deletions(-)

diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 64735bd..c95a878 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -2170,7 +2170,7 @@ static struct clk tegra_list_clks[] = {
 	PERIPH_CLK("i2s2",	"tegra-i2s.1",		NULL,	18,	0x104,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_out",	"spdif_out",		NULL,	10,	0x108,	100000000, mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_in",	"spdif_in",		NULL,	10,	0x10c,	100000000, mux_pllp_pllc_pllm,		MUX | DIV_U71),
-	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71 | MUX_PWM),
+	PERIPH_CLK("pwm",	"tegra-pwm",		NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71 | MUX_PWM),
 	PERIPH_CLK("spi",	"spi",			NULL,	43,	0x114,	40000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("xio",	"xio",			NULL,	45,	0x120,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("twc",	"twc",			NULL,	16,	0x12c,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
@@ -2269,10 +2269,6 @@ static struct clk_duplicate tegra_clk_duplicates[] = {
 	CLK_DUPLICATE("usbd", "tegra-otg", NULL),
 	CLK_DUPLICATE("hdmi", "tegradc.0", "hdmi"),
 	CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
-	CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
 	CLK_DUPLICATE("host1x", "tegra_grhost", "host1x"),
 	CLK_DUPLICATE("2d", "tegra_grhost", "gr2d"),
 	CLK_DUPLICATE("3d", "tegra_grhost", "gr3d"),
diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c
index 6d08b53..18afd0c 100644
--- a/arch/arm/mach-tegra/tegra30_clocks.c
+++ b/arch/arm/mach-tegra/tegra30_clocks.c
@@ -2886,7 +2886,7 @@ struct clk tegra_list_clks[] = {
 	PERIPH_CLK("i2s4",	"tegra30-i2s.4",	NULL,	102,	0x3c0,	26000000,  mux_pllaout0_audio4_2x_pllp_clkm,	MUX | DIV_U71 | PERIPH_ON_APB),
 	PERIPH_CLK("spdif_out",	"tegra30-spdif",	"spdif_out",	10,	0x108,	100000000, mux_pllaout0_audio_2x_pllp_clkm,	MUX | DIV_U71 | PERIPH_ON_APB),
 	PERIPH_CLK("spdif_in",	"tegra30-spdif",	"spdif_in",	10,	0x10c,	100000000, mux_pllp_pllc_pllm,		MUX | DIV_U71 | PERIPH_ON_APB),
-	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_clk32_clkm,	MUX | MUX_PWM | DIV_U71 | PERIPH_ON_APB),
+	PERIPH_CLK("pwm",	"tegra-pwm",		NULL,	17,	0x110,	432000000, mux_pllp_pllc_clk32_clkm,	MUX | MUX_PWM | DIV_U71 | PERIPH_ON_APB),
 	PERIPH_CLK("d_audio",	"tegra30-ahub",		"d_audio", 106,	0x3d0,	48000000,  mux_plla_pllc_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("dam0",	"tegra30-dam.0",	NULL,   108,	0x3d8,	48000000,  mux_plla_pllc_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("dam1",	"tegra30-dam.1",	NULL,   109,	0x3dc,	48000000,  mux_plla_pllc_pllp_clkm,	MUX | DIV_U71),
@@ -2990,10 +2990,6 @@ struct clk_duplicate tegra_clk_duplicates[] = {
 	CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
 	CLK_DUPLICATE("dsib", "tegradc.0", "dsib"),
 	CLK_DUPLICATE("dsia", "tegradc.1", "dsia"),
-	CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
-	CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
 	CLK_DUPLICATE("bsev", "tegra-avp", "bsev"),
 	CLK_DUPLICATE("bsev", "nvavp", "bsev"),
 	CLK_DUPLICATE("vde", "tegra-aes", "vde"),
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 08/17] pwm: Add NVIDIA Tegra SoC support
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

This commit adds a generic PWM framework driver for the PWFM controller
found on NVIDIA Tegra SoCs. The driver is based on code from the
Chromium kernel tree and was originally written by Gary King (NVIDIA)
and later modified by Simon Que (Chromium).

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
Acked-by: Stephen Warren <swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
---
Changes in v6:
- return EADDRNOTAVAIL if devm_request_and_ioremap() fails
- make tegra_pwm_ops constant

Changes in v5:
- use devm_request_and_ioremap()
- use clk_prepare_enable() and clk_disable_unprepare()
- call pwmchip_remove() after disabling PWM devices
- remove tegra_pwm_chip.enable[] field and use the PWM flags instead
- use pwm_readl()/pwm_writel() for register accesses and split out clock
  management to where it's needed
- add MODULE_ALIAS to enable automatic module loading

Changes in v4:
- merge patch from ChromiumOS kernel to fix a rounding issue
- move DT code to patch 7

Changes in v3:
- use pwm_device.hwpwm instead of recomputing it
- update pwm_ops for changes in patch 2

Changes in v2:
- set pwm_chip.dev field
- rename clk_enb to enable
- introduce NUM_PWM macro instead of hard-coding the number of PWM
  devices
- update comment in tegra_pwm_config
- fix coding-style for multi-line comments
- use module_platform_driver
- change license to GPL

 drivers/pwm/Kconfig     |   10 ++
 drivers/pwm/Makefile    |    1 +
 drivers/pwm/pwm-tegra.c |  254 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 265 insertions(+)
 create mode 100644 drivers/pwm/pwm-tegra.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 93c1052..bda6f23 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -9,4 +9,14 @@ menuconfig PWM
 
 if PWM
 
+config PWM_TEGRA
+	tristate "NVIDIA Tegra PWM support"
+	depends on ARCH_TEGRA
+	help
+	  Generic PWM framework driver for the PWFM controller found on NVIDIA
+	  Tegra SoCs.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-tegra.
+
 endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 3469c3d..12300f5 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_PWM)		+= core.o
+obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
new file mode 100644
index 0000000..0950142
--- /dev/null
+++ b/drivers/pwm/pwm-tegra.c
@@ -0,0 +1,254 @@
+/*
+ * drivers/pwm/pwm-tegra.c
+ *
+ * Tegra pulse-width-modulation controller driver
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PWM_ENABLE	(1 << 31)
+#define PWM_DUTY_WIDTH	8
+#define PWM_DUTY_SHIFT	16
+#define PWM_SCALE_WIDTH	13
+#define PWM_SCALE_SHIFT	0
+
+#define NUM_PWM 4
+
+struct tegra_pwm_chip {
+	struct pwm_chip		chip;
+	struct device		*dev;
+
+	struct clk		*clk;
+
+	void __iomem		*mmio_base;
+};
+
+static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip)
+{
+	return container_of(chip, struct tegra_pwm_chip, chip);
+}
+
+static inline u32 pwm_readl(struct tegra_pwm_chip *chip, unsigned int num)
+{
+	return readl(chip->mmio_base + (num << 4));
+}
+
+static inline void pwm_writel(struct tegra_pwm_chip *chip, unsigned int num,
+			     unsigned long val)
+{
+	writel(val, chip->mmio_base + (num << 4));
+}
+
+static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+			    int duty_ns, int period_ns)
+{
+	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+	unsigned long long c;
+	unsigned long rate, hz;
+	u32 val = 0;
+	int err;
+
+	/*
+	 * Convert from duty_ns / period_ns to a fixed number of duty ticks
+	 * per (1 << PWM_DUTY_WIDTH) cycles and make sure to round to the
+	 * nearest integer during division.
+	 */
+	c = duty_ns * ((1 << PWM_DUTY_WIDTH) - 1) + period_ns / 2;
+	do_div(c, period_ns);
+
+	val = (u32)c << PWM_DUTY_SHIFT;
+
+	/*
+	 * Compute the prescaler value for which (1 << PWM_DUTY_WIDTH)
+	 * cycles at the PWM clock rate will take period_ns nanoseconds.
+	 */
+	rate = clk_get_rate(pc->clk) >> PWM_DUTY_WIDTH;
+	hz = 1000000000ul / period_ns;
+
+	rate = (rate + (hz / 2)) / hz;
+
+	/*
+	 * Since the actual PWM divider is the register's frequency divider
+	 * field minus 1, we need to decrement to get the correct value to
+	 * write to the register.
+	 */
+	if (rate > 0)
+		rate--;
+
+	/*
+	 * Make sure that the rate will fit in the register's frequency
+	 * divider field.
+	 */
+	if (rate >> PWM_SCALE_WIDTH)
+		return -EINVAL;
+
+	val |= rate << PWM_SCALE_SHIFT;
+
+	/*
+	 * If the PWM channel is disabled, make sure to turn on the clock
+	 * before writing the register. Otherwise, keep it enabled.
+	 */
+	if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+		err = clk_prepare_enable(pc->clk);
+		if (err < 0)
+			return err;
+	} else
+		val |= PWM_ENABLE;
+
+	pwm_writel(pc, pwm->hwpwm, val);
+
+	/*
+	 * If the PWM is not enabled, turn the clock off again to save power.
+	 */
+	if (!test_bit(PWMF_ENABLED, &pwm->flags))
+		clk_disable_unprepare(pc->clk);
+
+	return 0;
+}
+
+static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+	int rc = 0;
+	u32 val;
+
+	rc = clk_prepare_enable(pc->clk);
+	if (rc < 0)
+		return rc;
+
+	val = pwm_readl(pc, pwm->hwpwm);
+	val |= PWM_ENABLE;
+	pwm_writel(pc, pwm->hwpwm, val);
+
+	return 0;
+}
+
+static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+	u32 val;
+
+	val = pwm_readl(pc, pwm->hwpwm);
+	val &= ~PWM_ENABLE;
+	pwm_writel(pc, pwm->hwpwm, val);
+
+	clk_disable_unprepare(pc->clk);
+}
+
+static const struct pwm_ops tegra_pwm_ops = {
+	.config = tegra_pwm_config,
+	.enable = tegra_pwm_enable,
+	.disable = tegra_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int tegra_pwm_probe(struct platform_device *pdev)
+{
+	struct tegra_pwm_chip *pwm;
+	struct resource *r;
+	int ret;
+
+	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+	if (!pwm) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	pwm->dev = &pdev->dev;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		dev_err(&pdev->dev, "no memory resources defined\n");
+		return -ENODEV;
+	}
+
+	pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+	if (!pwm->mmio_base) {
+		dev_err(&pdev->dev, "failed to ioremap() region\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	platform_set_drvdata(pdev, pwm);
+
+	pwm->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pwm->clk))
+		return PTR_ERR(pwm->clk);
+
+	pwm->chip.dev = &pdev->dev;
+	pwm->chip.ops = &tegra_pwm_ops;
+	pwm->chip.base = -1;
+	pwm->chip.npwm = NUM_PWM;
+
+	ret = pwmchip_add(&pwm->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+		clk_put(pwm->clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devexit tegra_pwm_remove(struct platform_device *pdev)
+{
+	struct tegra_pwm_chip *pc = platform_get_drvdata(pdev);
+	int i;
+
+	if (WARN_ON(!pc))
+		return -ENODEV;
+
+	for (i = 0; i < NUM_PWM; i++) {
+		struct pwm_device *pwm = &pc->chip.pwms[i];
+
+		if (!test_bit(PWMF_ENABLED, &pwm->flags))
+			if (clk_prepare_enable(pc->clk) < 0)
+				continue;
+
+		pwm_writel(pc, i, 0);
+
+		clk_disable_unprepare(pc->clk);
+	}
+
+	pwmchip_remove(&pc->chip);
+	clk_put(pc->clk);
+
+	return 0;
+}
+
+static struct platform_driver tegra_pwm_driver = {
+	.driver = {
+		.name = "tegra-pwm",
+	},
+	.probe = tegra_pwm_probe,
+	.remove = __devexit_p(tegra_pwm_remove),
+};
+
+module_platform_driver(tegra_pwm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_ALIAS("platform:tegra-pwm");
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 08/17] pwm: Add NVIDIA Tegra SoC support
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

This commit adds a generic PWM framework driver for the PWFM controller
found on NVIDIA Tegra SoCs. The driver is based on code from the
Chromium kernel tree and was originally written by Gary King (NVIDIA)
and later modified by Simon Que (Chromium).

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Stephen Warren <swarren@wwwdotorg.org>
---
Changes in v6:
- return EADDRNOTAVAIL if devm_request_and_ioremap() fails
- make tegra_pwm_ops constant

Changes in v5:
- use devm_request_and_ioremap()
- use clk_prepare_enable() and clk_disable_unprepare()
- call pwmchip_remove() after disabling PWM devices
- remove tegra_pwm_chip.enable[] field and use the PWM flags instead
- use pwm_readl()/pwm_writel() for register accesses and split out clock
  management to where it's needed
- add MODULE_ALIAS to enable automatic module loading

Changes in v4:
- merge patch from ChromiumOS kernel to fix a rounding issue
- move DT code to patch 7

Changes in v3:
- use pwm_device.hwpwm instead of recomputing it
- update pwm_ops for changes in patch 2

Changes in v2:
- set pwm_chip.dev field
- rename clk_enb to enable
- introduce NUM_PWM macro instead of hard-coding the number of PWM
  devices
- update comment in tegra_pwm_config
- fix coding-style for multi-line comments
- use module_platform_driver
- change license to GPL

 drivers/pwm/Kconfig     |   10 ++
 drivers/pwm/Makefile    |    1 +
 drivers/pwm/pwm-tegra.c |  254 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 265 insertions(+)
 create mode 100644 drivers/pwm/pwm-tegra.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 93c1052..bda6f23 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -9,4 +9,14 @@ menuconfig PWM
 
 if PWM
 
+config PWM_TEGRA
+	tristate "NVIDIA Tegra PWM support"
+	depends on ARCH_TEGRA
+	help
+	  Generic PWM framework driver for the PWFM controller found on NVIDIA
+	  Tegra SoCs.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-tegra.
+
 endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 3469c3d..12300f5 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_PWM)		+= core.o
+obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
new file mode 100644
index 0000000..0950142
--- /dev/null
+++ b/drivers/pwm/pwm-tegra.c
@@ -0,0 +1,254 @@
+/*
+ * drivers/pwm/pwm-tegra.c
+ *
+ * Tegra pulse-width-modulation controller driver
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PWM_ENABLE	(1 << 31)
+#define PWM_DUTY_WIDTH	8
+#define PWM_DUTY_SHIFT	16
+#define PWM_SCALE_WIDTH	13
+#define PWM_SCALE_SHIFT	0
+
+#define NUM_PWM 4
+
+struct tegra_pwm_chip {
+	struct pwm_chip		chip;
+	struct device		*dev;
+
+	struct clk		*clk;
+
+	void __iomem		*mmio_base;
+};
+
+static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip)
+{
+	return container_of(chip, struct tegra_pwm_chip, chip);
+}
+
+static inline u32 pwm_readl(struct tegra_pwm_chip *chip, unsigned int num)
+{
+	return readl(chip->mmio_base + (num << 4));
+}
+
+static inline void pwm_writel(struct tegra_pwm_chip *chip, unsigned int num,
+			     unsigned long val)
+{
+	writel(val, chip->mmio_base + (num << 4));
+}
+
+static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+			    int duty_ns, int period_ns)
+{
+	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+	unsigned long long c;
+	unsigned long rate, hz;
+	u32 val = 0;
+	int err;
+
+	/*
+	 * Convert from duty_ns / period_ns to a fixed number of duty ticks
+	 * per (1 << PWM_DUTY_WIDTH) cycles and make sure to round to the
+	 * nearest integer during division.
+	 */
+	c = duty_ns * ((1 << PWM_DUTY_WIDTH) - 1) + period_ns / 2;
+	do_div(c, period_ns);
+
+	val = (u32)c << PWM_DUTY_SHIFT;
+
+	/*
+	 * Compute the prescaler value for which (1 << PWM_DUTY_WIDTH)
+	 * cycles@the PWM clock rate will take period_ns nanoseconds.
+	 */
+	rate = clk_get_rate(pc->clk) >> PWM_DUTY_WIDTH;
+	hz = 1000000000ul / period_ns;
+
+	rate = (rate + (hz / 2)) / hz;
+
+	/*
+	 * Since the actual PWM divider is the register's frequency divider
+	 * field minus 1, we need to decrement to get the correct value to
+	 * write to the register.
+	 */
+	if (rate > 0)
+		rate--;
+
+	/*
+	 * Make sure that the rate will fit in the register's frequency
+	 * divider field.
+	 */
+	if (rate >> PWM_SCALE_WIDTH)
+		return -EINVAL;
+
+	val |= rate << PWM_SCALE_SHIFT;
+
+	/*
+	 * If the PWM channel is disabled, make sure to turn on the clock
+	 * before writing the register. Otherwise, keep it enabled.
+	 */
+	if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+		err = clk_prepare_enable(pc->clk);
+		if (err < 0)
+			return err;
+	} else
+		val |= PWM_ENABLE;
+
+	pwm_writel(pc, pwm->hwpwm, val);
+
+	/*
+	 * If the PWM is not enabled, turn the clock off again to save power.
+	 */
+	if (!test_bit(PWMF_ENABLED, &pwm->flags))
+		clk_disable_unprepare(pc->clk);
+
+	return 0;
+}
+
+static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+	int rc = 0;
+	u32 val;
+
+	rc = clk_prepare_enable(pc->clk);
+	if (rc < 0)
+		return rc;
+
+	val = pwm_readl(pc, pwm->hwpwm);
+	val |= PWM_ENABLE;
+	pwm_writel(pc, pwm->hwpwm, val);
+
+	return 0;
+}
+
+static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+	u32 val;
+
+	val = pwm_readl(pc, pwm->hwpwm);
+	val &= ~PWM_ENABLE;
+	pwm_writel(pc, pwm->hwpwm, val);
+
+	clk_disable_unprepare(pc->clk);
+}
+
+static const struct pwm_ops tegra_pwm_ops = {
+	.config = tegra_pwm_config,
+	.enable = tegra_pwm_enable,
+	.disable = tegra_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int tegra_pwm_probe(struct platform_device *pdev)
+{
+	struct tegra_pwm_chip *pwm;
+	struct resource *r;
+	int ret;
+
+	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+	if (!pwm) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	pwm->dev = &pdev->dev;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		dev_err(&pdev->dev, "no memory resources defined\n");
+		return -ENODEV;
+	}
+
+	pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+	if (!pwm->mmio_base) {
+		dev_err(&pdev->dev, "failed to ioremap() region\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	platform_set_drvdata(pdev, pwm);
+
+	pwm->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pwm->clk))
+		return PTR_ERR(pwm->clk);
+
+	pwm->chip.dev = &pdev->dev;
+	pwm->chip.ops = &tegra_pwm_ops;
+	pwm->chip.base = -1;
+	pwm->chip.npwm = NUM_PWM;
+
+	ret = pwmchip_add(&pwm->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+		clk_put(pwm->clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devexit tegra_pwm_remove(struct platform_device *pdev)
+{
+	struct tegra_pwm_chip *pc = platform_get_drvdata(pdev);
+	int i;
+
+	if (WARN_ON(!pc))
+		return -ENODEV;
+
+	for (i = 0; i < NUM_PWM; i++) {
+		struct pwm_device *pwm = &pc->chip.pwms[i];
+
+		if (!test_bit(PWMF_ENABLED, &pwm->flags))
+			if (clk_prepare_enable(pc->clk) < 0)
+				continue;
+
+		pwm_writel(pc, i, 0);
+
+		clk_disable_unprepare(pc->clk);
+	}
+
+	pwmchip_remove(&pc->chip);
+	clk_put(pc->clk);
+
+	return 0;
+}
+
+static struct platform_driver tegra_pwm_driver = {
+	.driver = {
+		.name = "tegra-pwm",
+	},
+	.probe = tegra_pwm_probe,
+	.remove = __devexit_p(tegra_pwm_remove),
+};
+
+module_platform_driver(tegra_pwm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_ALIAS("platform:tegra-pwm");
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 09/17] pwm: tegra: Add device tree support
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

Add auxdata to instantiate the PWFM controller from a device tree,
include the corresponding nodes in the dtsi files for Tegra 20 and
Tegra 30 and add binding documentation.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
Acked-by: Stephen Warren <swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
---
Changes in v5:
- rename tegra-pwm.txt to nvidia,tegra20-pwm.txt

Changes in v4:
- add DT binding documentation
- allow nvidia,tegra20-pwm as fallback for nvidia,tegra30-pwm
- export OF device table (for module auto-loading)

Changes in v3:
- add missing include for mach/iomap.h

 .../devicetree/bindings/pwm/nvidia,tegra20-pwm.txt |   18 ++++++++++++++++++
 arch/arm/boot/dts/tegra20.dtsi                     |    6 ++++++
 arch/arm/boot/dts/tegra30.dtsi                     |    6 ++++++
 arch/arm/mach-tegra/board-dt-tegra20.c             |    1 +
 arch/arm/mach-tegra/board-dt-tegra30.c             |    3 +++
 drivers/pwm/pwm-tegra.c                            |   11 +++++++++++
 6 files changed, 45 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt

diff --git a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
new file mode 100644
index 0000000..bbbeedb
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
@@ -0,0 +1,18 @@
+Tegra SoC PWFM controller
+
+Required properties:
+- compatible: should be one of:
+  - "nvidia,tegra20-pwm"
+  - "nvidia,tegra30-pwm"
+- reg: physical base address and length of the controller's registers
+- #pwm-cells: On Tegra the number of cells used to specify a PWM is 2. The
+  first cell specifies the per-chip index of the PWM to use and the second
+  cell is the duty cycle in nanoseconds.
+
+Example:
+
+	pwm: pwm@7000a000 {
+		compatible = "nvidia,tegra20-pwm";
+		reg = <0x7000a000 0x100>;
+		#pwm-cells = <2>;
+	};
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 108e894..55b28fd 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -154,6 +154,12 @@
 		interrupts = < 0 91 0x04 >;
 	};
 
+	pwm: pwm@7000a000 {
+		compatible = "nvidia,tegra20-pwm";
+		reg = <0x7000a000 0x100>;
+		#pwm-cells = <2>;
+	};
+
 	emc@7000f400 {
 		#address-cells = <1>;
 		#size-cells = <0>;
diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index 62a7b39..3fa5211 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -154,6 +154,12 @@
 		interrupts = < 0 91 0x04 >;
 	};
 
+	pwm: pwm@7000a000 {
+		compatible = "nvidia,tegra30-pwm", "nvidia,tegra20-pwm";
+		reg = <0x7000a000 0x100>;
+		#pwm-cells = <2>;
+	};
+
 	sdhci@78000000 {
 		compatible = "nvidia,tegra30-sdhci", "nvidia,tegra20-sdhci";
 		reg = <0x78000000 0x200>;
diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/board-dt-tegra20.c
index 0952494..87bdce5 100644
--- a/arch/arm/mach-tegra/board-dt-tegra20.c
+++ b/arch/arm/mach-tegra/board-dt-tegra20.c
@@ -73,6 +73,7 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
 		       &tegra_ehci2_pdata),
 	OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB3_BASE, "tegra-ehci.2",
 		       &tegra_ehci3_pdata),
+	OF_DEV_AUXDATA("nvidia,tegra20-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
 	{}
 };
 
diff --git a/arch/arm/mach-tegra/board-dt-tegra30.c b/arch/arm/mach-tegra/board-dt-tegra30.c
index 5f7c03e..5e1180f 100644
--- a/arch/arm/mach-tegra/board-dt-tegra30.c
+++ b/arch/arm/mach-tegra/board-dt-tegra30.c
@@ -33,6 +33,8 @@
 #include <asm/mach/arch.h>
 #include <asm/hardware/gic.h>
 
+#include <mach/iomap.h>
+
 #include "board.h"
 #include "clock.h"
 
@@ -51,6 +53,7 @@ struct of_dev_auxdata tegra30_auxdata_lookup[] __initdata = {
 	OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C500, "tegra-i2c.2", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C700, "tegra-i2c.3", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000D000, "tegra-i2c.4", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
 	{}
 };
 
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 0950142..472b74e 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -239,9 +239,20 @@ static int __devexit tegra_pwm_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static struct of_device_id tegra_pwm_of_match[] = {
+	{ .compatible = "nvidia,tegra20-pwm" },
+	{ .compatible = "nvidia,tegra30-pwm" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
+#endif
+
 static struct platform_driver tegra_pwm_driver = {
 	.driver = {
 		.name = "tegra-pwm",
+		.of_match_table = of_match_ptr(tegra_pwm_of_match),
 	},
 	.probe = tegra_pwm_probe,
 	.remove = __devexit_p(tegra_pwm_remove),
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 09/17] pwm: tegra: Add device tree support
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

Add auxdata to instantiate the PWFM controller from a device tree,
include the corresponding nodes in the dtsi files for Tegra 20 and
Tegra 30 and add binding documentation.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Stephen Warren <swarren@wwwdotorg.org>
---
Changes in v5:
- rename tegra-pwm.txt to nvidia,tegra20-pwm.txt

Changes in v4:
- add DT binding documentation
- allow nvidia,tegra20-pwm as fallback for nvidia,tegra30-pwm
- export OF device table (for module auto-loading)

Changes in v3:
- add missing include for mach/iomap.h

 .../devicetree/bindings/pwm/nvidia,tegra20-pwm.txt |   18 ++++++++++++++++++
 arch/arm/boot/dts/tegra20.dtsi                     |    6 ++++++
 arch/arm/boot/dts/tegra30.dtsi                     |    6 ++++++
 arch/arm/mach-tegra/board-dt-tegra20.c             |    1 +
 arch/arm/mach-tegra/board-dt-tegra30.c             |    3 +++
 drivers/pwm/pwm-tegra.c                            |   11 +++++++++++
 6 files changed, 45 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt

diff --git a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
new file mode 100644
index 0000000..bbbeedb
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
@@ -0,0 +1,18 @@
+Tegra SoC PWFM controller
+
+Required properties:
+- compatible: should be one of:
+  - "nvidia,tegra20-pwm"
+  - "nvidia,tegra30-pwm"
+- reg: physical base address and length of the controller's registers
+- #pwm-cells: On Tegra the number of cells used to specify a PWM is 2. The
+  first cell specifies the per-chip index of the PWM to use and the second
+  cell is the duty cycle in nanoseconds.
+
+Example:
+
+	pwm: pwm at 7000a000 {
+		compatible = "nvidia,tegra20-pwm";
+		reg = <0x7000a000 0x100>;
+		#pwm-cells = <2>;
+	};
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 108e894..55b28fd 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -154,6 +154,12 @@
 		interrupts = < 0 91 0x04 >;
 	};
 
+	pwm: pwm at 7000a000 {
+		compatible = "nvidia,tegra20-pwm";
+		reg = <0x7000a000 0x100>;
+		#pwm-cells = <2>;
+	};
+
 	emc at 7000f400 {
 		#address-cells = <1>;
 		#size-cells = <0>;
diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index 62a7b39..3fa5211 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -154,6 +154,12 @@
 		interrupts = < 0 91 0x04 >;
 	};
 
+	pwm: pwm at 7000a000 {
+		compatible = "nvidia,tegra30-pwm", "nvidia,tegra20-pwm";
+		reg = <0x7000a000 0x100>;
+		#pwm-cells = <2>;
+	};
+
 	sdhci at 78000000 {
 		compatible = "nvidia,tegra30-sdhci", "nvidia,tegra20-sdhci";
 		reg = <0x78000000 0x200>;
diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/board-dt-tegra20.c
index 0952494..87bdce5 100644
--- a/arch/arm/mach-tegra/board-dt-tegra20.c
+++ b/arch/arm/mach-tegra/board-dt-tegra20.c
@@ -73,6 +73,7 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
 		       &tegra_ehci2_pdata),
 	OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB3_BASE, "tegra-ehci.2",
 		       &tegra_ehci3_pdata),
+	OF_DEV_AUXDATA("nvidia,tegra20-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
 	{}
 };
 
diff --git a/arch/arm/mach-tegra/board-dt-tegra30.c b/arch/arm/mach-tegra/board-dt-tegra30.c
index 5f7c03e..5e1180f 100644
--- a/arch/arm/mach-tegra/board-dt-tegra30.c
+++ b/arch/arm/mach-tegra/board-dt-tegra30.c
@@ -33,6 +33,8 @@
 #include <asm/mach/arch.h>
 #include <asm/hardware/gic.h>
 
+#include <mach/iomap.h>
+
 #include "board.h"
 #include "clock.h"
 
@@ -51,6 +53,7 @@ struct of_dev_auxdata tegra30_auxdata_lookup[] __initdata = {
 	OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C500, "tegra-i2c.2", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C700, "tegra-i2c.3", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000D000, "tegra-i2c.4", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
 	{}
 };
 
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 0950142..472b74e 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -239,9 +239,20 @@ static int __devexit tegra_pwm_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static struct of_device_id tegra_pwm_of_match[] = {
+	{ .compatible = "nvidia,tegra20-pwm" },
+	{ .compatible = "nvidia,tegra30-pwm" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
+#endif
+
 static struct platform_driver tegra_pwm_driver = {
 	.driver = {
 		.name = "tegra-pwm",
+		.of_match_table = of_match_ptr(tegra_pwm_of_match),
 	},
 	.probe = tegra_pwm_probe,
 	.remove = __devexit_p(tegra_pwm_remove),
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 10/17] pwm: Move Blackfin PWM driver to PWM framework
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

This commit moves the Blackfin PWM driver to the drivers/pwm sub-
directory and converts it to register with the new PWM framework.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
 arch/blackfin/Kconfig                              |   10 --
 arch/blackfin/kernel/Makefile                      |    1 -
 drivers/pwm/Kconfig                                |    9 ++
 drivers/pwm/Makefile                               |    1 +
 .../kernel/pwm.c => drivers/pwm/pwm-bfin.c         |  138 ++++++++++++++------
 5 files changed, 111 insertions(+), 48 deletions(-)
 rename arch/blackfin/kernel/pwm.c => drivers/pwm/pwm-bfin.c (26%)

diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index 373a690..128357e 100644
--- a/arch/blackfin/Kconfig
+++ b/arch/blackfin/Kconfig
@@ -950,16 +950,6 @@ config BFIN_GPTIMERS
 	  To compile this driver as a module, choose M here: the module
 	  will be called gptimers.
 
-config HAVE_PWM
-	tristate "Enable PWM API support"
-	depends on BFIN_GPTIMERS
-	help
-	  Enable support for the Pulse Width Modulation framework (as
-	  found in linux/pwm.h).
-
-	  To compile this driver as a module, choose M here: the module
-	  will be called pwm.
-
 choice
 	prompt "Uncached DMA region"
 	default DMA_UNCACHED_1M
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile
index 9a0d6d7..00fef32 100644
--- a/arch/blackfin/kernel/Makefile
+++ b/arch/blackfin/kernel/Makefile
@@ -21,7 +21,6 @@ obj-$(CONFIG_FUNCTION_TRACER)        += ftrace-entry.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)  += ftrace.o
 CFLAGS_REMOVE_ftrace.o = -pg
 
-obj-$(CONFIG_HAVE_PWM)               += pwm.o
 obj-$(CONFIG_IPIPE)                  += ipipe.o
 obj-$(CONFIG_BFIN_GPTIMERS)          += gptimers.o
 obj-$(CONFIG_CPLB_INFO)              += cplbinfo.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index bda6f23..eb54042 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -9,6 +9,15 @@ menuconfig PWM
 
 if PWM
 
+config PWM_BFIN
+	tristate "Blackfin PWM support"
+	depends on BFIN_GPTIMERS
+	help
+	  Generic PWM framework driver for Blackfin.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-bfin.
+
 config PWM_TEGRA
 	tristate "NVIDIA Tegra PWM support"
 	depends on ARCH_TEGRA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 12300f5..251b8d2 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_PWM)		+= core.o
+obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/arch/blackfin/kernel/pwm.c b/drivers/pwm/pwm-bfin.c
similarity index 26%
rename from arch/blackfin/kernel/pwm.c
rename to drivers/pwm/pwm-bfin.c
index 33f5942..a0c6bf9 100644
--- a/arch/blackfin/kernel/pwm.c
+++ b/drivers/pwm/pwm-bfin.c
@@ -7,14 +7,18 @@
  */
 
 #include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
 
 #include <asm/gptimers.h>
 #include <asm/portmux.h>
 
-struct pwm_device {
-	unsigned id;
+struct bfin_pwm_chip {
+	struct pwm_chip chip;
+};
+
+struct bfin_pwm {
 	unsigned short pin;
 };
 
@@ -23,44 +27,45 @@ static const unsigned short pwm_to_gptimer_per[] = {
 	P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
 };
 
-struct pwm_device *pwm_request(int pwm_id, const char *label)
+static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	struct pwm_device *pwm;
+	struct bfin_pwm *priv;
 	int ret;
 
-	/* XXX: pwm_id really should be unsigned */
-	if (pwm_id < 0)
-		return NULL;
+	if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per))
+		return -EINVAL;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
 
-	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
-	if (!pwm)
-		return pwm;
+	priv->pin = pwm_to_gptimer_per[pwm->hwpwm];
 
-	pwm->id = pwm_id;
-	if (pwm->id >= ARRAY_SIZE(pwm_to_gptimer_per))
-		goto err;
+	ret = peripheral_request(priv->pin, NULL);
+	if (ret) {
+		kfree(priv);
+		return ret;
+	}
 
-	pwm->pin = pwm_to_gptimer_per[pwm->id];
-	ret = peripheral_request(pwm->pin, label);
-	if (ret)
-		goto err;
+	pwm_set_chip_data(pwm, priv);
 
-	return pwm;
- err:
-	kfree(pwm);
-	return NULL;
+	return 0;
 }
-EXPORT_SYMBOL(pwm_request);
 
-void pwm_free(struct pwm_device *pwm)
+static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	peripheral_free(pwm->pin);
-	kfree(pwm);
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	if (priv) {
+		peripheral_free(priv->pin);
+		kfree(priv);
+	}
 }
-EXPORT_SYMBOL(pwm_free);
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+		int duty_ns, int period_ns)
 {
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
 	unsigned long period, duty;
 	unsigned long long val;
 
@@ -78,23 +83,82 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	if (duty >= period)
 		duty = period - 1;
 
-	set_gptimer_config(pwm->id, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
-	set_gptimer_pwidth(pwm->id, duty);
-	set_gptimer_period(pwm->id, period);
+	set_gptimer_config(priv->pin, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
+	set_gptimer_pwidth(priv->pin, duty);
+	set_gptimer_period(priv->pin, period);
 
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	enable_gptimer(pwm->id);
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	enable_gptimer(priv->pin);
+
 	return 0;
 }
-EXPORT_SYMBOL(pwm_enable);
 
-void pwm_disable(struct pwm_device *pwm)
+static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	disable_gptimer(pwm->id);
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	disable_gptimer(priv->pin);
 }
-EXPORT_SYMBOL(pwm_disable);
+
+static struct pwm_ops bfin_pwm_ops = {
+	.request = bfin_pwm_request,
+	.free = bfin_pwm_free,
+	.config = bfin_pwm_config,
+	.enable = bfin_pwm_enable,
+	.disable = bfin_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int bfin_pwm_probe(struct platform_device *pdev)
+{
+	struct bfin_pwm_chip *pwm;
+	int ret;
+
+	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+	if (!pwm) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, pwm);
+
+	pwm->chip.dev = &pdev->dev;
+	pwm->chip.ops = &bfin_pwm_ops;
+	pwm->chip.base = -1;
+	pwm->chip.npwm = 12;
+
+	ret = pwmchip_add(&pwm->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devexit bfin_pwm_remove(struct platform_device *pdev)
+{
+	struct bfin_pwm_chip *pwm = platform_get_drvdata(pdev);
+
+	pwmchip_remove(&pwm->chip);
+
+	return 0;
+}
+
+static struct platform_driver bfin_pwm_driver = {
+	.driver = {
+		.name = "bfin-pwm",
+	},
+	.probe = bfin_pwm_probe,
+	.remove = __devexit_p(bfin_pwm_remove),
+};
+
+module_platform_driver(bfin_pwm_driver);
+
+MODULE_LICENSE("GPL");
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 10/17] pwm: Move Blackfin PWM driver to PWM framework
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

This commit moves the Blackfin PWM driver to the drivers/pwm sub-
directory and converts it to register with the new PWM framework.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
 arch/blackfin/Kconfig                              |   10 --
 arch/blackfin/kernel/Makefile                      |    1 -
 drivers/pwm/Kconfig                                |    9 ++
 drivers/pwm/Makefile                               |    1 +
 .../kernel/pwm.c => drivers/pwm/pwm-bfin.c         |  138 ++++++++++++++------
 5 files changed, 111 insertions(+), 48 deletions(-)
 rename arch/blackfin/kernel/pwm.c => drivers/pwm/pwm-bfin.c (26%)

diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index 373a690..128357e 100644
--- a/arch/blackfin/Kconfig
+++ b/arch/blackfin/Kconfig
@@ -950,16 +950,6 @@ config BFIN_GPTIMERS
 	  To compile this driver as a module, choose M here: the module
 	  will be called gptimers.
 
-config HAVE_PWM
-	tristate "Enable PWM API support"
-	depends on BFIN_GPTIMERS
-	help
-	  Enable support for the Pulse Width Modulation framework (as
-	  found in linux/pwm.h).
-
-	  To compile this driver as a module, choose M here: the module
-	  will be called pwm.
-
 choice
 	prompt "Uncached DMA region"
 	default DMA_UNCACHED_1M
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile
index 9a0d6d7..00fef32 100644
--- a/arch/blackfin/kernel/Makefile
+++ b/arch/blackfin/kernel/Makefile
@@ -21,7 +21,6 @@ obj-$(CONFIG_FUNCTION_TRACER)        += ftrace-entry.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)  += ftrace.o
 CFLAGS_REMOVE_ftrace.o = -pg
 
-obj-$(CONFIG_HAVE_PWM)               += pwm.o
 obj-$(CONFIG_IPIPE)                  += ipipe.o
 obj-$(CONFIG_BFIN_GPTIMERS)          += gptimers.o
 obj-$(CONFIG_CPLB_INFO)              += cplbinfo.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index bda6f23..eb54042 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -9,6 +9,15 @@ menuconfig PWM
 
 if PWM
 
+config PWM_BFIN
+	tristate "Blackfin PWM support"
+	depends on BFIN_GPTIMERS
+	help
+	  Generic PWM framework driver for Blackfin.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-bfin.
+
 config PWM_TEGRA
 	tristate "NVIDIA Tegra PWM support"
 	depends on ARCH_TEGRA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 12300f5..251b8d2 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_PWM)		+= core.o
+obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/arch/blackfin/kernel/pwm.c b/drivers/pwm/pwm-bfin.c
similarity index 26%
rename from arch/blackfin/kernel/pwm.c
rename to drivers/pwm/pwm-bfin.c
index 33f5942..a0c6bf9 100644
--- a/arch/blackfin/kernel/pwm.c
+++ b/drivers/pwm/pwm-bfin.c
@@ -7,14 +7,18 @@
  */
 
 #include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
 
 #include <asm/gptimers.h>
 #include <asm/portmux.h>
 
-struct pwm_device {
-	unsigned id;
+struct bfin_pwm_chip {
+	struct pwm_chip chip;
+};
+
+struct bfin_pwm {
 	unsigned short pin;
 };
 
@@ -23,44 +27,45 @@ static const unsigned short pwm_to_gptimer_per[] = {
 	P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
 };
 
-struct pwm_device *pwm_request(int pwm_id, const char *label)
+static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	struct pwm_device *pwm;
+	struct bfin_pwm *priv;
 	int ret;
 
-	/* XXX: pwm_id really should be unsigned */
-	if (pwm_id < 0)
-		return NULL;
+	if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per))
+		return -EINVAL;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
 
-	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
-	if (!pwm)
-		return pwm;
+	priv->pin = pwm_to_gptimer_per[pwm->hwpwm];
 
-	pwm->id = pwm_id;
-	if (pwm->id >= ARRAY_SIZE(pwm_to_gptimer_per))
-		goto err;
+	ret = peripheral_request(priv->pin, NULL);
+	if (ret) {
+		kfree(priv);
+		return ret;
+	}
 
-	pwm->pin = pwm_to_gptimer_per[pwm->id];
-	ret = peripheral_request(pwm->pin, label);
-	if (ret)
-		goto err;
+	pwm_set_chip_data(pwm, priv);
 
-	return pwm;
- err:
-	kfree(pwm);
-	return NULL;
+	return 0;
 }
-EXPORT_SYMBOL(pwm_request);
 
-void pwm_free(struct pwm_device *pwm)
+static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	peripheral_free(pwm->pin);
-	kfree(pwm);
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	if (priv) {
+		peripheral_free(priv->pin);
+		kfree(priv);
+	}
 }
-EXPORT_SYMBOL(pwm_free);
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+		int duty_ns, int period_ns)
 {
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
 	unsigned long period, duty;
 	unsigned long long val;
 
@@ -78,23 +83,82 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	if (duty >= period)
 		duty = period - 1;
 
-	set_gptimer_config(pwm->id, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
-	set_gptimer_pwidth(pwm->id, duty);
-	set_gptimer_period(pwm->id, period);
+	set_gptimer_config(priv->pin, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
+	set_gptimer_pwidth(priv->pin, duty);
+	set_gptimer_period(priv->pin, period);
 
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	enable_gptimer(pwm->id);
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	enable_gptimer(priv->pin);
+
 	return 0;
 }
-EXPORT_SYMBOL(pwm_enable);
 
-void pwm_disable(struct pwm_device *pwm)
+static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	disable_gptimer(pwm->id);
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	disable_gptimer(priv->pin);
 }
-EXPORT_SYMBOL(pwm_disable);
+
+static struct pwm_ops bfin_pwm_ops = {
+	.request = bfin_pwm_request,
+	.free = bfin_pwm_free,
+	.config = bfin_pwm_config,
+	.enable = bfin_pwm_enable,
+	.disable = bfin_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int bfin_pwm_probe(struct platform_device *pdev)
+{
+	struct bfin_pwm_chip *pwm;
+	int ret;
+
+	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+	if (!pwm) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, pwm);
+
+	pwm->chip.dev = &pdev->dev;
+	pwm->chip.ops = &bfin_pwm_ops;
+	pwm->chip.base = -1;
+	pwm->chip.npwm = 12;
+
+	ret = pwmchip_add(&pwm->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devexit bfin_pwm_remove(struct platform_device *pdev)
+{
+	struct bfin_pwm_chip *pwm = platform_get_drvdata(pdev);
+
+	pwmchip_remove(&pwm->chip);
+
+	return 0;
+}
+
+static struct platform_driver bfin_pwm_driver = {
+	.driver = {
+		.name = "bfin-pwm",
+	},
+	.probe = bfin_pwm_probe,
+	.remove = __devexit_p(bfin_pwm_remove),
+};
+
+module_platform_driver(bfin_pwm_driver);
+
+MODULE_LICENSE("GPL");
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 11/17] pwm: Move PXA PWM driver to PWM framework
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

This commit moves the PXA PWM driver to the drivers/pwm subdirectory and
converts it to use the new PWM framework.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
Changes in v5:
- replace __raw_readl()/__raw_writel() by readl()/writel()
- use clk_prepare_enable() and clk_disable_unprepare()

Changes in v3:
- update PWM ops for changes in patch 2

 arch/arm/plat-pxa/Makefile                       |    1 -
 drivers/pwm/Kconfig                              |    9 ++
 drivers/pwm/Makefile                             |    1 +
 arch/arm/plat-pxa/pwm.c => drivers/pwm/pwm-pxa.c |  172 ++++++++--------------
 4 files changed, 68 insertions(+), 115 deletions(-)
 rename arch/arm/plat-pxa/pwm.c => drivers/pwm/pwm-pxa.c (57%)

diff --git a/arch/arm/plat-pxa/Makefile b/arch/arm/plat-pxa/Makefile
index f302d04..af8e484 100644
--- a/arch/arm/plat-pxa/Makefile
+++ b/arch/arm/plat-pxa/Makefile
@@ -8,5 +8,4 @@ obj-$(CONFIG_PXA3xx)		+= mfp.o
 obj-$(CONFIG_PXA95x)		+= mfp.o
 obj-$(CONFIG_ARCH_MMP)		+= mfp.o
 
-obj-$(CONFIG_HAVE_PWM)		+= pwm.o
 obj-$(CONFIG_PXA_SSP)		+= ssp.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index eb54042..0ef4f30 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -18,6 +18,15 @@ config PWM_BFIN
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-bfin.
 
+config PWM_PXA
+	tristate "PXA PWM support"
+	depends on ARCH_PXA
+	help
+	  Generic PWM framework driver for PXA.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-pxa.
+
 config PWM_TEGRA
 	tristate "NVIDIA Tegra PWM support"
 	depends on ARCH_TEGRA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 251b8d2..e859c51 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
+obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/arch/arm/plat-pxa/pwm.c b/drivers/pwm/pwm-pxa.c
similarity index 57%
rename from arch/arm/plat-pxa/pwm.c
rename to drivers/pwm/pwm-pxa.c
index ef32686..d5c6ce5 100644
--- a/arch/arm/plat-pxa/pwm.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -43,33 +43,39 @@ MODULE_DEVICE_TABLE(platform, pwm_id_table);
 #define PWMCR_SD	(1 << 6)
 #define PWMDCR_FD	(1 << 10)
 
-struct pwm_device {
-	struct list_head	node;
-	struct pwm_device	*secondary;
-	struct platform_device	*pdev;
+struct pxa_pwm_chip {
+	struct pwm_chip	chip;
+	struct device	*dev;
 
-	const char	*label;
 	struct clk	*clk;
 	int		clk_enabled;
 	void __iomem	*mmio_base;
-
-	unsigned int	use_count;
-	unsigned int	pwm_id;
 };
 
+static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip)
+{
+	return container_of(chip, struct pxa_pwm_chip, chip);
+}
+
 /*
  * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
  * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
  */
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+			  int duty_ns, int period_ns)
 {
+	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
 	unsigned long long c;
 	unsigned long period_cycles, prescale, pv, dc;
+	unsigned long offset;
+	int rc;
 
-	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+	if (period_ns == 0 || duty_ns > period_ns)
 		return -EINVAL;
 
-	c = clk_get_rate(pwm->clk);
+	offset = pwm->hwpwm ? 0x10 : 0;
+
+	c = clk_get_rate(pc->clk);
 	c = c * period_ns;
 	do_div(c, 1000000000);
 	period_cycles = c;
@@ -90,98 +96,56 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	/* NOTE: the clock to PWM has to be enabled first
 	 * before writing to the registers
 	 */
-	clk_enable(pwm->clk);
-	__raw_writel(prescale, pwm->mmio_base + PWMCR);
-	__raw_writel(dc, pwm->mmio_base + PWMDCR);
-	__raw_writel(pv, pwm->mmio_base + PWMPCR);
-	clk_disable(pwm->clk);
+	rc = clk_prepare_enable(pc->clk);
+	if (rc < 0)
+		return rc;
+
+	writel(prescale, pc->mmio_base + offset + PWMCR);
+	writel(dc, pc->mmio_base + offset + PWMDCR);
+	writel(pv, pc->mmio_base + offset + PWMPCR);
 
+	clk_disable_unprepare(pc->clk);
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
 	int rc = 0;
 
-	if (!pwm->clk_enabled) {
-		rc = clk_enable(pwm->clk);
+	if (!pc->clk_enabled) {
+		rc = clk_prepare_enable(pc->clk);
 		if (!rc)
-			pwm->clk_enabled = 1;
+			pc->clk_enabled++;
 	}
 	return rc;
 }
-EXPORT_SYMBOL(pwm_enable);
 
-void pwm_disable(struct pwm_device *pwm)
+static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	if (pwm->clk_enabled) {
-		clk_disable(pwm->clk);
-		pwm->clk_enabled = 0;
-	}
-}
-EXPORT_SYMBOL(pwm_disable);
+	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
 
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int found = 0;
-
-	mutex_lock(&pwm_lock);
-
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
+	if (pc->clk_enabled) {
+		clk_disable_unprepare(pc->clk);
+		pc->clk_enabled--;
 	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count++;
-			pwm->label = label;
-		} else
-			pwm = ERR_PTR(-EBUSY);
-	} else
-		pwm = ERR_PTR(-ENOENT);
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
 }
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
 
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else
-		pr_warning("PWM device already freed\n");
-
-	mutex_unlock(&pwm_lock);
-}
-EXPORT_SYMBOL(pwm_free);
-
-static inline void __add_pwm(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->node, &pwm_list);
-	mutex_unlock(&pwm_lock);
-}
+static struct pwm_ops pxa_pwm_ops = {
+	.config = pxa_pwm_config,
+	.enable = pxa_pwm_enable,
+	.disable = pxa_pwm_disable,
+	.owner = THIS_MODULE,
+};
 
 static int __devinit pwm_probe(struct platform_device *pdev)
 {
 	const struct platform_device_id *id = platform_get_device_id(pdev);
-	struct pwm_device *pwm, *secondary = NULL;
+	struct pxa_pwm_chip *pwm;
 	struct resource *r;
 	int ret = 0;
 
-	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
 	if (pwm == NULL) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
@@ -194,9 +158,10 @@ static int __devinit pwm_probe(struct platform_device *pdev)
 	}
 	pwm->clk_enabled = 0;
 
-	pwm->use_count = 0;
-	pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id;
-	pwm->pdev = pdev;
+	pwm->chip.dev = &pdev->dev;
+	pwm->chip.ops = &pxa_pwm_ops;
+	pwm->chip.base = -1;
+	pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (r == NULL) {
@@ -219,25 +184,12 @@ static int __devinit pwm_probe(struct platform_device *pdev)
 		goto err_free_mem;
 	}
 
-	if (id->driver_data & HAS_SECONDARY_PWM) {
-		secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-		if (secondary == NULL) {
-			ret = -ENOMEM;
-			goto err_free_mem;
-		}
-
-		*secondary = *pwm;
-		pwm->secondary = secondary;
-
-		/* registers for the second PWM has offset of 0x10 */
-		secondary->mmio_base = pwm->mmio_base + 0x10;
-		secondary->pwm_id = pdev->id + 2;
+	ret = pwmchip_add(&pwm->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+		return ret;
 	}
 
-	__add_pwm(pwm);
-	if (secondary)
-		__add_pwm(secondary);
-
 	platform_set_drvdata(pdev, pwm);
 	return 0;
 
@@ -252,30 +204,22 @@ err_free:
 
 static int __devexit pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwm;
+	struct pxa_pwm_chip *chip;
 	struct resource *r;
 
-	pwm = platform_get_drvdata(pdev);
-	if (pwm == NULL)
+	chip = platform_get_drvdata(pdev);
+	if (chip == NULL)
 		return -ENODEV;
 
-	mutex_lock(&pwm_lock);
-
-	if (pwm->secondary) {
-		list_del(&pwm->secondary->node);
-		kfree(pwm->secondary);
-	}
+	pwmchip_remove(&chip->chip);
 
-	list_del(&pwm->node);
-	mutex_unlock(&pwm_lock);
-
-	iounmap(pwm->mmio_base);
+	iounmap(chip->mmio_base);
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, resource_size(r));
 
-	clk_put(pwm->clk);
-	kfree(pwm);
+	clk_put(chip->clk);
+	kfree(chip);
 	return 0;
 }
 
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 11/17] pwm: Move PXA PWM driver to PWM framework
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

This commit moves the PXA PWM driver to the drivers/pwm subdirectory and
converts it to use the new PWM framework.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
Changes in v5:
- replace __raw_readl()/__raw_writel() by readl()/writel()
- use clk_prepare_enable() and clk_disable_unprepare()

Changes in v3:
- update PWM ops for changes in patch 2

 arch/arm/plat-pxa/Makefile                       |    1 -
 drivers/pwm/Kconfig                              |    9 ++
 drivers/pwm/Makefile                             |    1 +
 arch/arm/plat-pxa/pwm.c => drivers/pwm/pwm-pxa.c |  172 ++++++++--------------
 4 files changed, 68 insertions(+), 115 deletions(-)
 rename arch/arm/plat-pxa/pwm.c => drivers/pwm/pwm-pxa.c (57%)

diff --git a/arch/arm/plat-pxa/Makefile b/arch/arm/plat-pxa/Makefile
index f302d04..af8e484 100644
--- a/arch/arm/plat-pxa/Makefile
+++ b/arch/arm/plat-pxa/Makefile
@@ -8,5 +8,4 @@ obj-$(CONFIG_PXA3xx)		+= mfp.o
 obj-$(CONFIG_PXA95x)		+= mfp.o
 obj-$(CONFIG_ARCH_MMP)		+= mfp.o
 
-obj-$(CONFIG_HAVE_PWM)		+= pwm.o
 obj-$(CONFIG_PXA_SSP)		+= ssp.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index eb54042..0ef4f30 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -18,6 +18,15 @@ config PWM_BFIN
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-bfin.
 
+config PWM_PXA
+	tristate "PXA PWM support"
+	depends on ARCH_PXA
+	help
+	  Generic PWM framework driver for PXA.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-pxa.
+
 config PWM_TEGRA
 	tristate "NVIDIA Tegra PWM support"
 	depends on ARCH_TEGRA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 251b8d2..e859c51 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
+obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/arch/arm/plat-pxa/pwm.c b/drivers/pwm/pwm-pxa.c
similarity index 57%
rename from arch/arm/plat-pxa/pwm.c
rename to drivers/pwm/pwm-pxa.c
index ef32686..d5c6ce5 100644
--- a/arch/arm/plat-pxa/pwm.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -43,33 +43,39 @@ MODULE_DEVICE_TABLE(platform, pwm_id_table);
 #define PWMCR_SD	(1 << 6)
 #define PWMDCR_FD	(1 << 10)
 
-struct pwm_device {
-	struct list_head	node;
-	struct pwm_device	*secondary;
-	struct platform_device	*pdev;
+struct pxa_pwm_chip {
+	struct pwm_chip	chip;
+	struct device	*dev;
 
-	const char	*label;
 	struct clk	*clk;
 	int		clk_enabled;
 	void __iomem	*mmio_base;
-
-	unsigned int	use_count;
-	unsigned int	pwm_id;
 };
 
+static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip)
+{
+	return container_of(chip, struct pxa_pwm_chip, chip);
+}
+
 /*
  * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
  * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
  */
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+			  int duty_ns, int period_ns)
 {
+	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
 	unsigned long long c;
 	unsigned long period_cycles, prescale, pv, dc;
+	unsigned long offset;
+	int rc;
 
-	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+	if (period_ns == 0 || duty_ns > period_ns)
 		return -EINVAL;
 
-	c = clk_get_rate(pwm->clk);
+	offset = pwm->hwpwm ? 0x10 : 0;
+
+	c = clk_get_rate(pc->clk);
 	c = c * period_ns;
 	do_div(c, 1000000000);
 	period_cycles = c;
@@ -90,98 +96,56 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	/* NOTE: the clock to PWM has to be enabled first
 	 * before writing to the registers
 	 */
-	clk_enable(pwm->clk);
-	__raw_writel(prescale, pwm->mmio_base + PWMCR);
-	__raw_writel(dc, pwm->mmio_base + PWMDCR);
-	__raw_writel(pv, pwm->mmio_base + PWMPCR);
-	clk_disable(pwm->clk);
+	rc = clk_prepare_enable(pc->clk);
+	if (rc < 0)
+		return rc;
+
+	writel(prescale, pc->mmio_base + offset + PWMCR);
+	writel(dc, pc->mmio_base + offset + PWMDCR);
+	writel(pv, pc->mmio_base + offset + PWMPCR);
 
+	clk_disable_unprepare(pc->clk);
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
 	int rc = 0;
 
-	if (!pwm->clk_enabled) {
-		rc = clk_enable(pwm->clk);
+	if (!pc->clk_enabled) {
+		rc = clk_prepare_enable(pc->clk);
 		if (!rc)
-			pwm->clk_enabled = 1;
+			pc->clk_enabled++;
 	}
 	return rc;
 }
-EXPORT_SYMBOL(pwm_enable);
 
-void pwm_disable(struct pwm_device *pwm)
+static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	if (pwm->clk_enabled) {
-		clk_disable(pwm->clk);
-		pwm->clk_enabled = 0;
-	}
-}
-EXPORT_SYMBOL(pwm_disable);
+	struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
 
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int found = 0;
-
-	mutex_lock(&pwm_lock);
-
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
+	if (pc->clk_enabled) {
+		clk_disable_unprepare(pc->clk);
+		pc->clk_enabled--;
 	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count++;
-			pwm->label = label;
-		} else
-			pwm = ERR_PTR(-EBUSY);
-	} else
-		pwm = ERR_PTR(-ENOENT);
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
 }
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
 
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else
-		pr_warning("PWM device already freed\n");
-
-	mutex_unlock(&pwm_lock);
-}
-EXPORT_SYMBOL(pwm_free);
-
-static inline void __add_pwm(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->node, &pwm_list);
-	mutex_unlock(&pwm_lock);
-}
+static struct pwm_ops pxa_pwm_ops = {
+	.config = pxa_pwm_config,
+	.enable = pxa_pwm_enable,
+	.disable = pxa_pwm_disable,
+	.owner = THIS_MODULE,
+};
 
 static int __devinit pwm_probe(struct platform_device *pdev)
 {
 	const struct platform_device_id *id = platform_get_device_id(pdev);
-	struct pwm_device *pwm, *secondary = NULL;
+	struct pxa_pwm_chip *pwm;
 	struct resource *r;
 	int ret = 0;
 
-	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
 	if (pwm == NULL) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
@@ -194,9 +158,10 @@ static int __devinit pwm_probe(struct platform_device *pdev)
 	}
 	pwm->clk_enabled = 0;
 
-	pwm->use_count = 0;
-	pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id;
-	pwm->pdev = pdev;
+	pwm->chip.dev = &pdev->dev;
+	pwm->chip.ops = &pxa_pwm_ops;
+	pwm->chip.base = -1;
+	pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (r == NULL) {
@@ -219,25 +184,12 @@ static int __devinit pwm_probe(struct platform_device *pdev)
 		goto err_free_mem;
 	}
 
-	if (id->driver_data & HAS_SECONDARY_PWM) {
-		secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-		if (secondary == NULL) {
-			ret = -ENOMEM;
-			goto err_free_mem;
-		}
-
-		*secondary = *pwm;
-		pwm->secondary = secondary;
-
-		/* registers for the second PWM has offset of 0x10 */
-		secondary->mmio_base = pwm->mmio_base + 0x10;
-		secondary->pwm_id = pdev->id + 2;
+	ret = pwmchip_add(&pwm->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+		return ret;
 	}
 
-	__add_pwm(pwm);
-	if (secondary)
-		__add_pwm(secondary);
-
 	platform_set_drvdata(pdev, pwm);
 	return 0;
 
@@ -252,30 +204,22 @@ err_free:
 
 static int __devexit pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwm;
+	struct pxa_pwm_chip *chip;
 	struct resource *r;
 
-	pwm = platform_get_drvdata(pdev);
-	if (pwm == NULL)
+	chip = platform_get_drvdata(pdev);
+	if (chip == NULL)
 		return -ENODEV;
 
-	mutex_lock(&pwm_lock);
-
-	if (pwm->secondary) {
-		list_del(&pwm->secondary->node);
-		kfree(pwm->secondary);
-	}
+	pwmchip_remove(&chip->chip);
 
-	list_del(&pwm->node);
-	mutex_unlock(&pwm_lock);
-
-	iounmap(pwm->mmio_base);
+	iounmap(chip->mmio_base);
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, resource_size(r));
 
-	clk_put(pwm->clk);
-	kfree(pwm);
+	clk_put(chip->clk);
+	kfree(chip);
 	return 0;
 }
 
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 12/17] ARM i.MX: Move i.MX pwm driver to pwm framework
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

From: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

Move the driver to drivers/pwm/ and convert it to use the framework.

Signed-off-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
Changes in v5:
- adopt patch by Sascha Hauer to move i.MX driver to PWM framework
- rename to imx_pwm internally

 arch/arm/plat-mxc/Kconfig                        |    6 -
 arch/arm/plat-mxc/Makefile                       |    1 -
 drivers/pwm/Kconfig                              |    9 ++
 drivers/pwm/Makefile                             |    1 +
 arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c |  182 +++++++++-------------
 5 files changed, 81 insertions(+), 118 deletions(-)
 rename arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c (58%)

diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig
index c722f9c..baf9064 100644
--- a/arch/arm/plat-mxc/Kconfig
+++ b/arch/arm/plat-mxc/Kconfig
@@ -47,12 +47,6 @@ config MXC_TZIC
 config MXC_AVIC
 	bool
 
-config MXC_PWM
-	tristate "Enable PWM driver"
-	select HAVE_PWM
-	help
-	  Enable support for the i.MX PWM controller(s).
-
 config MXC_DEBUG_BOARD
 	bool "Enable MXC debug board(for 3-stack)"
 	help
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index e81290c..46ba8de 100644
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_MXC_AVIC) += avic.o
 obj-$(CONFIG_IMX_HAVE_IOMUX_V1) += iomux-v1.o
 obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o
 obj-$(CONFIG_IRAM_ALLOC) += iram_alloc.o
-obj-$(CONFIG_MXC_PWM)  += pwm.o
 obj-$(CONFIG_MXC_ULPI) += ulpi.o
 obj-$(CONFIG_MXC_USE_EPIT) += epit.o
 obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0ef4f30..a9a9715 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -18,6 +18,15 @@ config PWM_BFIN
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-bfin.
 
+config PWM_IMX
+	tristate "i.MX pwm support"
+	depends on ARCH_MXC
+	help
+	  Generic PWM framework driver for i.MX.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-imx.
+
 config PWM_PXA
 	tristate "PXA PWM support"
 	depends on ARCH_PXA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index e859c51..3dd1b83 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
+obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/arch/arm/plat-mxc/pwm.c b/drivers/pwm/pwm-imx.c
similarity index 58%
rename from arch/arm/plat-mxc/pwm.c
rename to drivers/pwm/pwm-imx.c
index c0cab22..ef9c51b 100644
--- a/arch/arm/plat-mxc/pwm.c
+++ b/drivers/pwm/pwm-imx.c
@@ -39,33 +39,28 @@
 #define MX3_PWMCR_CLKSRC_IPG      (1 << 16)
 #define MX3_PWMCR_EN              (1 << 0)
 
-
-
-struct pwm_device {
-	struct list_head	node;
-	struct platform_device *pdev;
-
-	const char	*label;
+struct imx_chip {
 	struct clk	*clk;
 
 	int		clk_enabled;
 	void __iomem	*mmio_base;
 
-	unsigned int	use_count;
-	unsigned int	pwm_id;
+	struct pwm_chip	chip;
 };
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+#define to_imx_chip(chip)	container_of(chip, struct imx_chip, chip)
+
+static int imx_pwm_config(struct pwm_chip *chip,
+		struct pwm_device *pwm, int duty_ns, int period_ns)
 {
-	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
-		return -EINVAL;
+	struct imx_chip *imx = to_imx_chip(chip);
 
 	if (!(cpu_is_mx1() || cpu_is_mx21())) {
 		unsigned long long c;
 		unsigned long period_cycles, duty_cycles, prescale;
 		u32 cr;
 
-		c = clk_get_rate(pwm->clk);
+		c = clk_get_rate(imx->clk);
 		c = c * period_ns;
 		do_div(c, 1000000000);
 		period_cycles = c;
@@ -86,8 +81,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		else
 			period_cycles = 0;
 
-		writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
-		writel(period_cycles, pwm->mmio_base + MX3_PWMPR);
+		writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
+		writel(period_cycles, imx->mmio_base + MX3_PWMPR);
 
 		cr = MX3_PWMCR_PRESCALER(prescale) |
 			MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
@@ -98,7 +93,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		else
 			cr |= MX3_PWMCR_CLKSRC_IPG_HIGH;
 
-		writel(cr, pwm->mmio_base + MX3_PWMCR);
+		writel(cr, imx->mmio_base + MX3_PWMCR);
 	} else if (cpu_is_mx1() || cpu_is_mx21()) {
 		/* The PWM subsystem allows for exact frequencies. However,
 		 * I cannot connect a scope on my device to the PWM line and
@@ -116,110 +111,72 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		 * both the prescaler (/1 .. /128) and then by CLKSEL
 		 * (/2 .. /16).
 		 */
-		u32 max = readl(pwm->mmio_base + MX1_PWMP);
+		u32 max = readl(imx->mmio_base + MX1_PWMP);
 		u32 p = max * duty_ns / period_ns;
-		writel(max - p, pwm->mmio_base + MX1_PWMS);
+		writel(max - p, imx->mmio_base + MX1_PWMS);
 	} else {
 		BUG();
 	}
 
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct imx_chip *imx = to_imx_chip(chip);
 	int rc = 0;
 
-	if (!pwm->clk_enabled) {
-		rc = clk_prepare_enable(pwm->clk);
+	if (!imx->clk_enabled) {
+		rc = clk_prepare_enable(imx->clk);
 		if (!rc)
-			pwm->clk_enabled = 1;
+			imx->clk_enabled = 1;
 	}
 	return rc;
 }
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-	writel(0, pwm->mmio_base + MX3_PWMCR);
-
-	if (pwm->clk_enabled) {
-		clk_disable_unprepare(pwm->clk);
-		pwm->clk_enabled = 0;
-	}
-}
-EXPORT_SYMBOL(pwm_disable);
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
 
-struct pwm_device *pwm_request(int pwm_id, const char *label)
+static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	struct pwm_device *pwm;
-	int found = 0;
+	struct imx_chip *imx = to_imx_chip(chip);
 
-	mutex_lock(&pwm_lock);
+	writel(0, imx->mmio_base + MX3_PWMCR);
 
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
+	if (imx->clk_enabled) {
+		clk_disable_unprepare(imx->clk);
+		imx->clk_enabled = 0;
 	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count++;
-			pwm->label = label;
-		} else
-			pwm = ERR_PTR(-EBUSY);
-	} else
-		pwm = ERR_PTR(-ENOENT);
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
 }
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else
-		pr_warning("PWM device already freed\n");
 
-	mutex_unlock(&pwm_lock);
-}
-EXPORT_SYMBOL(pwm_free);
+static struct pwm_ops imx_pwm_ops = {
+	.enable = imx_pwm_enable,
+	.disable = imx_pwm_disable,
+	.config = imx_pwm_config,
+	.owner = THIS_MODULE,
+};
 
-static int __devinit mxc_pwm_probe(struct platform_device *pdev)
+static int __devinit imx_pwm_probe(struct platform_device *pdev)
 {
-	struct pwm_device *pwm;
+	struct imx_chip *imx;
 	struct resource *r;
 	int ret = 0;
 
-	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-	if (pwm == NULL) {
+	imx = kzalloc(sizeof(*imx), GFP_KERNEL);
+	if (imx == NULL) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	pwm->clk = clk_get(&pdev->dev, "pwm");
+	imx->clk = clk_get(&pdev->dev, "pwm");
 
-	if (IS_ERR(pwm->clk)) {
-		ret = PTR_ERR(pwm->clk);
+	if (IS_ERR(imx->clk)) {
+		ret = PTR_ERR(imx->clk);
 		goto err_free;
 	}
 
-	pwm->clk_enabled = 0;
+	imx->chip.ops = &imx_pwm_ops;
+	imx->chip.base = -1;
+	imx->chip.npwm = 1;
 
-	pwm->use_count = 0;
-	pwm->pwm_id = pdev->id;
-	pwm->pdev = pdev;
+	imx->clk_enabled = 0;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (r == NULL) {
@@ -235,72 +192,75 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev)
 		goto err_free_clk;
 	}
 
-	pwm->mmio_base = ioremap(r->start, resource_size(r));
-	if (pwm->mmio_base == NULL) {
+	imx->mmio_base = ioremap(r->start, resource_size(r));
+	if (imx->mmio_base == NULL) {
 		dev_err(&pdev->dev, "failed to ioremap() registers\n");
 		ret = -ENODEV;
 		goto err_free_mem;
 	}
 
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->node, &pwm_list);
-	mutex_unlock(&pwm_lock);
+	ret = pwmchip_add(&imx->chip);
+	if (ret)
+		goto err_iounmap;
 
-	platform_set_drvdata(pdev, pwm);
+	platform_set_drvdata(pdev, imx);
 	return 0;
 
+err_iounmap:
+	iounmap(imx->mmio_base);
 err_free_mem:
 	release_mem_region(r->start, resource_size(r));
 err_free_clk:
-	clk_put(pwm->clk);
+	clk_put(imx->clk);
 err_free:
-	kfree(pwm);
+	kfree(imx);
 	return ret;
 }
 
-static int __devexit mxc_pwm_remove(struct platform_device *pdev)
+static int __devexit imx_pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwm;
+	struct imx_chip *imx;
 	struct resource *r;
+	int ret;
 
-	pwm = platform_get_drvdata(pdev);
-	if (pwm == NULL)
+	imx = platform_get_drvdata(pdev);
+	if (imx == NULL)
 		return -ENODEV;
 
-	mutex_lock(&pwm_lock);
-	list_del(&pwm->node);
-	mutex_unlock(&pwm_lock);
+	ret = pwmchip_remove(&imx->chip);
+	if (ret)
+		return ret;
 
-	iounmap(pwm->mmio_base);
+	iounmap(imx->mmio_base);
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, resource_size(r));
 
-	clk_put(pwm->clk);
+	clk_put(imx->clk);
 
-	kfree(pwm);
+	kfree(imx);
 	return 0;
 }
 
-static struct platform_driver mxc_pwm_driver = {
+static struct platform_driver imx_pwm_driver = {
 	.driver		= {
 		.name	= "mxc_pwm",
 	},
-	.probe		= mxc_pwm_probe,
-	.remove		= __devexit_p(mxc_pwm_remove),
+	.probe		= imx_pwm_probe,
+	.remove		= __devexit_p(imx_pwm_remove),
 };
 
-static int __init mxc_pwm_init(void)
+static int __init imx_pwm_init(void)
 {
-	return platform_driver_register(&mxc_pwm_driver);
+	return platform_driver_register(&imx_pwm_driver);
 }
-arch_initcall(mxc_pwm_init);
+arch_initcall(imx_pwm_init);
 
-static void __exit mxc_pwm_exit(void)
+static void __exit imx_pwm_exit(void)
 {
-	platform_driver_unregister(&mxc_pwm_driver);
+	platform_driver_unregister(&imx_pwm_driver);
 }
-module_exit(mxc_pwm_exit);
+module_exit(imx_pwm_exit);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 12/17] ARM i.MX: Move i.MX pwm driver to pwm framework
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

From: Sascha Hauer <s.hauer@pengutronix.de>

Move the driver to drivers/pwm/ and convert it to use the framework.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
Changes in v5:
- adopt patch by Sascha Hauer to move i.MX driver to PWM framework
- rename to imx_pwm internally

 arch/arm/plat-mxc/Kconfig                        |    6 -
 arch/arm/plat-mxc/Makefile                       |    1 -
 drivers/pwm/Kconfig                              |    9 ++
 drivers/pwm/Makefile                             |    1 +
 arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c |  182 +++++++++-------------
 5 files changed, 81 insertions(+), 118 deletions(-)
 rename arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c (58%)

diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig
index c722f9c..baf9064 100644
--- a/arch/arm/plat-mxc/Kconfig
+++ b/arch/arm/plat-mxc/Kconfig
@@ -47,12 +47,6 @@ config MXC_TZIC
 config MXC_AVIC
 	bool
 
-config MXC_PWM
-	tristate "Enable PWM driver"
-	select HAVE_PWM
-	help
-	  Enable support for the i.MX PWM controller(s).
-
 config MXC_DEBUG_BOARD
 	bool "Enable MXC debug board(for 3-stack)"
 	help
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index e81290c..46ba8de 100644
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_MXC_AVIC) += avic.o
 obj-$(CONFIG_IMX_HAVE_IOMUX_V1) += iomux-v1.o
 obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o
 obj-$(CONFIG_IRAM_ALLOC) += iram_alloc.o
-obj-$(CONFIG_MXC_PWM)  += pwm.o
 obj-$(CONFIG_MXC_ULPI) += ulpi.o
 obj-$(CONFIG_MXC_USE_EPIT) += epit.o
 obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0ef4f30..a9a9715 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -18,6 +18,15 @@ config PWM_BFIN
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-bfin.
 
+config PWM_IMX
+	tristate "i.MX pwm support"
+	depends on ARCH_MXC
+	help
+	  Generic PWM framework driver for i.MX.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-imx.
+
 config PWM_PXA
 	tristate "PXA PWM support"
 	depends on ARCH_PXA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index e859c51..3dd1b83 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
+obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/arch/arm/plat-mxc/pwm.c b/drivers/pwm/pwm-imx.c
similarity index 58%
rename from arch/arm/plat-mxc/pwm.c
rename to drivers/pwm/pwm-imx.c
index c0cab22..ef9c51b 100644
--- a/arch/arm/plat-mxc/pwm.c
+++ b/drivers/pwm/pwm-imx.c
@@ -39,33 +39,28 @@
 #define MX3_PWMCR_CLKSRC_IPG      (1 << 16)
 #define MX3_PWMCR_EN              (1 << 0)
 
-
-
-struct pwm_device {
-	struct list_head	node;
-	struct platform_device *pdev;
-
-	const char	*label;
+struct imx_chip {
 	struct clk	*clk;
 
 	int		clk_enabled;
 	void __iomem	*mmio_base;
 
-	unsigned int	use_count;
-	unsigned int	pwm_id;
+	struct pwm_chip	chip;
 };
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+#define to_imx_chip(chip)	container_of(chip, struct imx_chip, chip)
+
+static int imx_pwm_config(struct pwm_chip *chip,
+		struct pwm_device *pwm, int duty_ns, int period_ns)
 {
-	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
-		return -EINVAL;
+	struct imx_chip *imx = to_imx_chip(chip);
 
 	if (!(cpu_is_mx1() || cpu_is_mx21())) {
 		unsigned long long c;
 		unsigned long period_cycles, duty_cycles, prescale;
 		u32 cr;
 
-		c = clk_get_rate(pwm->clk);
+		c = clk_get_rate(imx->clk);
 		c = c * period_ns;
 		do_div(c, 1000000000);
 		period_cycles = c;
@@ -86,8 +81,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		else
 			period_cycles = 0;
 
-		writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
-		writel(period_cycles, pwm->mmio_base + MX3_PWMPR);
+		writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
+		writel(period_cycles, imx->mmio_base + MX3_PWMPR);
 
 		cr = MX3_PWMCR_PRESCALER(prescale) |
 			MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
@@ -98,7 +93,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		else
 			cr |= MX3_PWMCR_CLKSRC_IPG_HIGH;
 
-		writel(cr, pwm->mmio_base + MX3_PWMCR);
+		writel(cr, imx->mmio_base + MX3_PWMCR);
 	} else if (cpu_is_mx1() || cpu_is_mx21()) {
 		/* The PWM subsystem allows for exact frequencies. However,
 		 * I cannot connect a scope on my device to the PWM line and
@@ -116,110 +111,72 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		 * both the prescaler (/1 .. /128) and then by CLKSEL
 		 * (/2 .. /16).
 		 */
-		u32 max = readl(pwm->mmio_base + MX1_PWMP);
+		u32 max = readl(imx->mmio_base + MX1_PWMP);
 		u32 p = max * duty_ns / period_ns;
-		writel(max - p, pwm->mmio_base + MX1_PWMS);
+		writel(max - p, imx->mmio_base + MX1_PWMS);
 	} else {
 		BUG();
 	}
 
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct imx_chip *imx = to_imx_chip(chip);
 	int rc = 0;
 
-	if (!pwm->clk_enabled) {
-		rc = clk_prepare_enable(pwm->clk);
+	if (!imx->clk_enabled) {
+		rc = clk_prepare_enable(imx->clk);
 		if (!rc)
-			pwm->clk_enabled = 1;
+			imx->clk_enabled = 1;
 	}
 	return rc;
 }
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-	writel(0, pwm->mmio_base + MX3_PWMCR);
-
-	if (pwm->clk_enabled) {
-		clk_disable_unprepare(pwm->clk);
-		pwm->clk_enabled = 0;
-	}
-}
-EXPORT_SYMBOL(pwm_disable);
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
 
-struct pwm_device *pwm_request(int pwm_id, const char *label)
+static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	struct pwm_device *pwm;
-	int found = 0;
+	struct imx_chip *imx = to_imx_chip(chip);
 
-	mutex_lock(&pwm_lock);
+	writel(0, imx->mmio_base + MX3_PWMCR);
 
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
+	if (imx->clk_enabled) {
+		clk_disable_unprepare(imx->clk);
+		imx->clk_enabled = 0;
 	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count++;
-			pwm->label = label;
-		} else
-			pwm = ERR_PTR(-EBUSY);
-	} else
-		pwm = ERR_PTR(-ENOENT);
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
 }
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else
-		pr_warning("PWM device already freed\n");
 
-	mutex_unlock(&pwm_lock);
-}
-EXPORT_SYMBOL(pwm_free);
+static struct pwm_ops imx_pwm_ops = {
+	.enable = imx_pwm_enable,
+	.disable = imx_pwm_disable,
+	.config = imx_pwm_config,
+	.owner = THIS_MODULE,
+};
 
-static int __devinit mxc_pwm_probe(struct platform_device *pdev)
+static int __devinit imx_pwm_probe(struct platform_device *pdev)
 {
-	struct pwm_device *pwm;
+	struct imx_chip *imx;
 	struct resource *r;
 	int ret = 0;
 
-	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-	if (pwm == NULL) {
+	imx = kzalloc(sizeof(*imx), GFP_KERNEL);
+	if (imx == NULL) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	pwm->clk = clk_get(&pdev->dev, "pwm");
+	imx->clk = clk_get(&pdev->dev, "pwm");
 
-	if (IS_ERR(pwm->clk)) {
-		ret = PTR_ERR(pwm->clk);
+	if (IS_ERR(imx->clk)) {
+		ret = PTR_ERR(imx->clk);
 		goto err_free;
 	}
 
-	pwm->clk_enabled = 0;
+	imx->chip.ops = &imx_pwm_ops;
+	imx->chip.base = -1;
+	imx->chip.npwm = 1;
 
-	pwm->use_count = 0;
-	pwm->pwm_id = pdev->id;
-	pwm->pdev = pdev;
+	imx->clk_enabled = 0;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (r == NULL) {
@@ -235,72 +192,75 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev)
 		goto err_free_clk;
 	}
 
-	pwm->mmio_base = ioremap(r->start, resource_size(r));
-	if (pwm->mmio_base == NULL) {
+	imx->mmio_base = ioremap(r->start, resource_size(r));
+	if (imx->mmio_base == NULL) {
 		dev_err(&pdev->dev, "failed to ioremap() registers\n");
 		ret = -ENODEV;
 		goto err_free_mem;
 	}
 
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->node, &pwm_list);
-	mutex_unlock(&pwm_lock);
+	ret = pwmchip_add(&imx->chip);
+	if (ret)
+		goto err_iounmap;
 
-	platform_set_drvdata(pdev, pwm);
+	platform_set_drvdata(pdev, imx);
 	return 0;
 
+err_iounmap:
+	iounmap(imx->mmio_base);
 err_free_mem:
 	release_mem_region(r->start, resource_size(r));
 err_free_clk:
-	clk_put(pwm->clk);
+	clk_put(imx->clk);
 err_free:
-	kfree(pwm);
+	kfree(imx);
 	return ret;
 }
 
-static int __devexit mxc_pwm_remove(struct platform_device *pdev)
+static int __devexit imx_pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwm;
+	struct imx_chip *imx;
 	struct resource *r;
+	int ret;
 
-	pwm = platform_get_drvdata(pdev);
-	if (pwm == NULL)
+	imx = platform_get_drvdata(pdev);
+	if (imx == NULL)
 		return -ENODEV;
 
-	mutex_lock(&pwm_lock);
-	list_del(&pwm->node);
-	mutex_unlock(&pwm_lock);
+	ret = pwmchip_remove(&imx->chip);
+	if (ret)
+		return ret;
 
-	iounmap(pwm->mmio_base);
+	iounmap(imx->mmio_base);
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, resource_size(r));
 
-	clk_put(pwm->clk);
+	clk_put(imx->clk);
 
-	kfree(pwm);
+	kfree(imx);
 	return 0;
 }
 
-static struct platform_driver mxc_pwm_driver = {
+static struct platform_driver imx_pwm_driver = {
 	.driver		= {
 		.name	= "mxc_pwm",
 	},
-	.probe		= mxc_pwm_probe,
-	.remove		= __devexit_p(mxc_pwm_remove),
+	.probe		= imx_pwm_probe,
+	.remove		= __devexit_p(imx_pwm_remove),
 };
 
-static int __init mxc_pwm_init(void)
+static int __init imx_pwm_init(void)
 {
-	return platform_driver_register(&mxc_pwm_driver);
+	return platform_driver_register(&imx_pwm_driver);
 }
-arch_initcall(mxc_pwm_init);
+arch_initcall(imx_pwm_init);
 
-static void __exit mxc_pwm_exit(void)
+static void __exit imx_pwm_exit(void)
 {
-	platform_driver_unregister(&mxc_pwm_driver);
+	platform_driver_unregister(&imx_pwm_driver);
 }
-module_exit(mxc_pwm_exit);
+module_exit(imx_pwm_exit);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 13/17] ARM Samsung: Move s3c pwm driver to pwm framework
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: Sascha Hauer, Ben Dooks, Kukjin Kim,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

From: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

Move the driver to drivers/pwm/ and convert it to use the framework.

Signed-off-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Cc: Ben Dooks <ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org>
Cc: Kukjin Kim <kgene.kim-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
Changes in v5:
- adopt patch by Sascha Hauer to move s3c driver to PWM framework
- This driver could use some cleanups and rework to actually take advantage
  of the multiple PWM devices per PWM chip feature. Currently, information
  about the controlling timers is passed via platform resources but is not
  used. Perhaps the corresponding information can be passed in a better way
  via platform data?

 arch/arm/plat-samsung/Makefile                     |    4 -
 drivers/pwm/Kconfig                                |    9 +
 drivers/pwm/Makefile                               |    1 +
 .../pwm.c => drivers/pwm/pwm-samsung.c             |  228 ++++++++------------
 4 files changed, 97 insertions(+), 145 deletions(-)
 rename arch/arm/plat-samsung/pwm.c => drivers/pwm/pwm-samsung.c (58%)

diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile
index 6012366..f09a7cf 100644
--- a/arch/arm/plat-samsung/Makefile
+++ b/arch/arm/plat-samsung/Makefile
@@ -50,7 +50,3 @@ obj-$(CONFIG_SAMSUNG_WAKEMASK)	+= wakeup-mask.o
 # PD support
 
 obj-$(CONFIG_SAMSUNG_PD)	+= pd.o
-
-# PWM support
-
-obj-$(CONFIG_HAVE_PWM)		+= pwm.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index a9a9715..0d9aa92 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -36,6 +36,15 @@ config PWM_PXA
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-pxa.
 
+config PWM_SAMSUNG
+	tristate "Samsung pwm support"
+	depends on PLAT_SAMSUNG
+	help
+	  Generic PWM framework driver for Samsung.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-samsung.
+
 config PWM_TEGRA
 	tristate "NVIDIA Tegra PWM support"
 	depends on ARCH_TEGRA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 3dd1b83..a1d87b1 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -2,4 +2,5 @@ obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
 obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
+obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/arch/arm/plat-samsung/pwm.c b/drivers/pwm/pwm-samsung.c
similarity index 58%
rename from arch/arm/plat-samsung/pwm.c
rename to drivers/pwm/pwm-samsung.c
index c559d84..631edf0 100644
--- a/arch/arm/plat-samsung/pwm.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -24,8 +24,7 @@
 
 #include <plat/regs-timer.h>
 
-struct pwm_device {
-	struct list_head	 list;
+struct s3c_chip {
 	struct platform_device	*pdev;
 
 	struct clk		*clk_div;
@@ -36,117 +35,65 @@ struct pwm_device {
 	unsigned int		 duty_ns;
 
 	unsigned char		 tcon_base;
-	unsigned char		 running;
-	unsigned char		 use_count;
 	unsigned char		 pwm_id;
+	struct pwm_chip		 chip;
 };
 
+#define to_s3c_chip(chip)	container_of(chip, struct s3c_chip, chip)
+
 #define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg)
 
 static struct clk *clk_scaler[2];
 
-static inline int pwm_is_tdiv(struct pwm_device *pwm)
-{
-	return clk_get_parent(pwm->clk) == pwm->clk_div;
-}
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int found = 0;
-
-	mutex_lock(&pwm_lock);
-
-	list_for_each_entry(pwm, &pwm_list, list) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
-	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count = 1;
-			pwm->label = label;
-		} else
-			pwm = ERR_PTR(-EBUSY);
-	} else
-		pwm = ERR_PTR(-ENOENT);
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
-}
-
-EXPORT_SYMBOL(pwm_request);
-
-
-void pwm_free(struct pwm_device *pwm)
+static inline int pwm_is_tdiv(struct s3c_chip *chip)
 {
-	mutex_lock(&pwm_lock);
-
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else
-		printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id);
-
-	mutex_unlock(&pwm_lock);
+	return clk_get_parent(chip->clk) == chip->clk_div;
 }
 
-EXPORT_SYMBOL(pwm_free);
-
 #define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0))
 #define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2))
 #define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3))
 #define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1))
 
-int pwm_enable(struct pwm_device *pwm)
+static int s3c_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct s3c_chip *s3c = to_s3c_chip(chip);
 	unsigned long flags;
 	unsigned long tcon;
 
 	local_irq_save(flags);
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_start(pwm);
+	tcon |= pwm_tcon_start(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
 
-	pwm->running = 1;
 	return 0;
 }
 
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
+static void s3c_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct s3c_chip *s3c = to_s3c_chip(chip);
 	unsigned long flags;
 	unsigned long tcon;
 
 	local_irq_save(flags);
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon &= ~pwm_tcon_start(pwm);
+	tcon &= ~pwm_tcon_start(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
-
-	pwm->running = 0;
 }
 
-EXPORT_SYMBOL(pwm_disable);
-
-static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq)
+static unsigned long pwm_calc_tin(struct s3c_chip *s3c, unsigned long freq)
 {
 	unsigned long tin_parent_rate;
 	unsigned int div;
 
-	tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div));
-	pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate);
+	tin_parent_rate = clk_get_rate(clk_get_parent(s3c->clk_div));
+	pwm_dbg(s3c, "tin parent at %lu\n", tin_parent_rate);
 
 	for (div = 2; div <= 16; div *= 2) {
 		if ((tin_parent_rate / (div << 16)) < freq)
@@ -158,8 +105,10 @@ static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq)
 
 #define NS_IN_HZ (1000000000UL)
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+		int duty_ns, int period_ns)
 {
+	struct s3c_chip *s3c = to_s3c_chip(chip);
 	unsigned long tin_rate;
 	unsigned long tin_ns;
 	unsigned long period;
@@ -178,38 +127,38 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	if (duty_ns > period_ns)
 		return -EINVAL;
 
-	if (period_ns == pwm->period_ns &&
-	    duty_ns == pwm->duty_ns)
+	if (period_ns == s3c->period_ns &&
+	    duty_ns == s3c->duty_ns)
 		return 0;
 
 	/* The TCMP and TCNT can be read without a lock, they're not
 	 * shared between the timers. */
 
-	tcmp = __raw_readl(S3C2410_TCMPB(pwm->pwm_id));
-	tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id));
+	tcmp = __raw_readl(S3C2410_TCMPB(s3c->pwm_id));
+	tcnt = __raw_readl(S3C2410_TCNTB(s3c->pwm_id));
 
 	period = NS_IN_HZ / period_ns;
 
-	pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n",
+	pwm_dbg(s3c, "duty_ns=%d, period_ns=%d (%lu)\n",
 		duty_ns, period_ns, period);
 
 	/* Check to see if we are changing the clock rate of the PWM */
 
-	if (pwm->period_ns != period_ns) {
-		if (pwm_is_tdiv(pwm)) {
-			tin_rate = pwm_calc_tin(pwm, period);
-			clk_set_rate(pwm->clk_div, tin_rate);
+	if (s3c->period_ns != period_ns) {
+		if (pwm_is_tdiv(s3c)) {
+			tin_rate = pwm_calc_tin(s3c, period);
+			clk_set_rate(s3c->clk_div, tin_rate);
 		} else
-			tin_rate = clk_get_rate(pwm->clk);
+			tin_rate = clk_get_rate(s3c->clk);
 
-		pwm->period_ns = period_ns;
+		s3c->period_ns = period_ns;
 
-		pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate);
+		pwm_dbg(s3c, "tin_rate=%lu\n", tin_rate);
 
 		tin_ns = NS_IN_HZ / tin_rate;
 		tcnt = period_ns / tin_ns;
 	} else
-		tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk);
+		tin_ns = NS_IN_HZ / clk_get_rate(s3c->clk);
 
 	/* Note, counters count down */
 
@@ -220,7 +169,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	if (tcmp == tcnt)
 		tcmp--;
 
-	pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
+	pwm_dbg(s3c, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
 
 	if (tcmp < 0)
 		tcmp = 0;
@@ -229,15 +178,15 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 
 	local_irq_save(flags);
 
-	__raw_writel(tcmp, S3C2410_TCMPB(pwm->pwm_id));
-	__raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id));
+	__raw_writel(tcmp, S3C2410_TCMPB(s3c->pwm_id));
+	__raw_writel(tcnt, S3C2410_TCNTB(s3c->pwm_id));
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_manulupdate(pwm);
-	tcon |= pwm_tcon_autoreload(pwm);
+	tcon |= pwm_tcon_manulupdate(s3c);
+	tcon |= pwm_tcon_autoreload(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
-	tcon &= ~pwm_tcon_manulupdate(pwm);
+	tcon &= ~pwm_tcon_manulupdate(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
@@ -245,24 +194,17 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	return 0;
 }
 
-EXPORT_SYMBOL(pwm_config);
-
-static int pwm_register(struct pwm_device *pwm)
-{
-	pwm->duty_ns = -1;
-	pwm->period_ns = -1;
-
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->list, &pwm_list);
-	mutex_unlock(&pwm_lock);
-
-	return 0;
-}
+static struct pwm_ops s3c_pwm_ops = {
+	.enable = s3c_pwm_enable,
+	.disable = s3c_pwm_disable,
+	.config = s3c_pwm_config,
+	.owner = THIS_MODULE,
+};
 
 static int s3c_pwm_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct pwm_device *pwm;
+	struct s3c_chip *s3c;
 	unsigned long flags;
 	unsigned long tcon;
 	unsigned int id = pdev->id;
@@ -273,83 +215,87 @@ static int s3c_pwm_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
-	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-	if (pwm == NULL) {
+	s3c = kzalloc(sizeof(*s3c), GFP_KERNEL);
+	if (s3c == NULL) {
 		dev_err(dev, "failed to allocate pwm_device\n");
 		return -ENOMEM;
 	}
 
-	pwm->pdev = pdev;
-	pwm->pwm_id = id;
-
 	/* calculate base of control bits in TCON */
-	pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;
+	s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4;
+	s3c->chip.ops = &s3c_pwm_ops;
+	s3c->chip.base = -1;
+	s3c->chip.npwm = 1;
 
-	pwm->clk = clk_get(dev, "pwm-tin");
-	if (IS_ERR(pwm->clk)) {
+	s3c->clk = clk_get(dev, "pwm-tin");
+	if (IS_ERR(s3c->clk)) {
 		dev_err(dev, "failed to get pwm tin clk\n");
-		ret = PTR_ERR(pwm->clk);
+		ret = PTR_ERR(s3c->clk);
 		goto err_alloc;
 	}
 
-	pwm->clk_div = clk_get(dev, "pwm-tdiv");
-	if (IS_ERR(pwm->clk_div)) {
+	s3c->clk_div = clk_get(dev, "pwm-tdiv");
+	if (IS_ERR(s3c->clk_div)) {
 		dev_err(dev, "failed to get pwm tdiv clk\n");
-		ret = PTR_ERR(pwm->clk_div);
+		ret = PTR_ERR(s3c->clk_div);
 		goto err_clk_tin;
 	}
 
-	clk_enable(pwm->clk);
-	clk_enable(pwm->clk_div);
+	clk_enable(s3c->clk);
+	clk_enable(s3c->clk_div);
 
 	local_irq_save(flags);
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_invert(pwm);
+	tcon |= pwm_tcon_invert(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
 
-
-	ret = pwm_register(pwm);
+	ret = pwmchip_add(&s3c->chip);
 	if (ret) {
 		dev_err(dev, "failed to register pwm\n");
 		goto err_clk_tdiv;
 	}
 
-	pwm_dbg(pwm, "config bits %02x\n",
-		(__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f);
+	pwm_dbg(s3c, "config bits %02x\n",
+		(__raw_readl(S3C2410_TCON) >> s3c->tcon_base) & 0x0f);
 
 	dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n",
-		 clk_get_rate(pwm->clk),
-		 clk_get_rate(pwm->clk_div),
-		 pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base);
+		 clk_get_rate(s3c->clk),
+		 clk_get_rate(s3c->clk_div),
+		 pwm_is_tdiv(s3c) ? "div" : "ext", s3c->tcon_base);
 
-	platform_set_drvdata(pdev, pwm);
+	platform_set_drvdata(pdev, s3c);
 	return 0;
 
  err_clk_tdiv:
-	clk_disable(pwm->clk_div);
-	clk_disable(pwm->clk);
-	clk_put(pwm->clk_div);
+	clk_disable(s3c->clk_div);
+	clk_disable(s3c->clk);
+	clk_put(s3c->clk_div);
 
  err_clk_tin:
-	clk_put(pwm->clk);
+	clk_put(s3c->clk);
 
  err_alloc:
-	kfree(pwm);
+	kfree(s3c);
 	return ret;
 }
 
 static int __devexit s3c_pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwm = platform_get_drvdata(pdev);
+	struct s3c_chip *s3c = platform_get_drvdata(pdev);
+	int err;
+
+	err = pwmchip_remove(&s3c->chip);
+	if (err < 0)
+		return err;
 
-	clk_disable(pwm->clk_div);
-	clk_disable(pwm->clk);
-	clk_put(pwm->clk_div);
-	clk_put(pwm->clk);
-	kfree(pwm);
+	clk_disable(s3c->clk_div);
+	clk_disable(s3c->clk);
+	clk_put(s3c->clk_div);
+	clk_put(s3c->clk);
+	kfree(s3c);
 
 	return 0;
 }
@@ -357,26 +303,26 @@ static int __devexit s3c_pwm_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
 {
-	struct pwm_device *pwm = platform_get_drvdata(pdev);
+	struct s3c_chip *s3c = platform_get_drvdata(pdev);
 
 	/* No one preserve these values during suspend so reset them
 	 * Otherwise driver leaves PWM unconfigured if same values
 	 * passed to pwm_config
 	 */
-	pwm->period_ns = 0;
-	pwm->duty_ns = 0;
+	s3c->period_ns = 0;
+	s3c->duty_ns = 0;
 
 	return 0;
 }
 
 static int s3c_pwm_resume(struct platform_device *pdev)
 {
-	struct pwm_device *pwm = platform_get_drvdata(pdev);
+	struct s3c_chip *s3c = platform_get_drvdata(pdev);
 	unsigned long tcon;
 
 	/* Restore invertion */
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_invert(pwm);
+	tcon |= pwm_tcon_invert(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	return 0;
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 13/17] ARM Samsung: Move s3c pwm driver to pwm framework
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

From: Sascha Hauer <s.hauer@pengutronix.de>

Move the driver to drivers/pwm/ and convert it to use the framework.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
Changes in v5:
- adopt patch by Sascha Hauer to move s3c driver to PWM framework
- This driver could use some cleanups and rework to actually take advantage
  of the multiple PWM devices per PWM chip feature. Currently, information
  about the controlling timers is passed via platform resources but is not
  used. Perhaps the corresponding information can be passed in a better way
  via platform data?

 arch/arm/plat-samsung/Makefile                     |    4 -
 drivers/pwm/Kconfig                                |    9 +
 drivers/pwm/Makefile                               |    1 +
 .../pwm.c => drivers/pwm/pwm-samsung.c             |  228 ++++++++------------
 4 files changed, 97 insertions(+), 145 deletions(-)
 rename arch/arm/plat-samsung/pwm.c => drivers/pwm/pwm-samsung.c (58%)

diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile
index 6012366..f09a7cf 100644
--- a/arch/arm/plat-samsung/Makefile
+++ b/arch/arm/plat-samsung/Makefile
@@ -50,7 +50,3 @@ obj-$(CONFIG_SAMSUNG_WAKEMASK)	+= wakeup-mask.o
 # PD support
 
 obj-$(CONFIG_SAMSUNG_PD)	+= pd.o
-
-# PWM support
-
-obj-$(CONFIG_HAVE_PWM)		+= pwm.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index a9a9715..0d9aa92 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -36,6 +36,15 @@ config PWM_PXA
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-pxa.
 
+config PWM_SAMSUNG
+	tristate "Samsung pwm support"
+	depends on PLAT_SAMSUNG
+	help
+	  Generic PWM framework driver for Samsung.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-samsung.
+
 config PWM_TEGRA
 	tristate "NVIDIA Tegra PWM support"
 	depends on ARCH_TEGRA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 3dd1b83..a1d87b1 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -2,4 +2,5 @@ obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
 obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
+obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/arch/arm/plat-samsung/pwm.c b/drivers/pwm/pwm-samsung.c
similarity index 58%
rename from arch/arm/plat-samsung/pwm.c
rename to drivers/pwm/pwm-samsung.c
index c559d84..631edf0 100644
--- a/arch/arm/plat-samsung/pwm.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -24,8 +24,7 @@
 
 #include <plat/regs-timer.h>
 
-struct pwm_device {
-	struct list_head	 list;
+struct s3c_chip {
 	struct platform_device	*pdev;
 
 	struct clk		*clk_div;
@@ -36,117 +35,65 @@ struct pwm_device {
 	unsigned int		 duty_ns;
 
 	unsigned char		 tcon_base;
-	unsigned char		 running;
-	unsigned char		 use_count;
 	unsigned char		 pwm_id;
+	struct pwm_chip		 chip;
 };
 
+#define to_s3c_chip(chip)	container_of(chip, struct s3c_chip, chip)
+
 #define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg)
 
 static struct clk *clk_scaler[2];
 
-static inline int pwm_is_tdiv(struct pwm_device *pwm)
-{
-	return clk_get_parent(pwm->clk) == pwm->clk_div;
-}
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int found = 0;
-
-	mutex_lock(&pwm_lock);
-
-	list_for_each_entry(pwm, &pwm_list, list) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
-	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count = 1;
-			pwm->label = label;
-		} else
-			pwm = ERR_PTR(-EBUSY);
-	} else
-		pwm = ERR_PTR(-ENOENT);
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
-}
-
-EXPORT_SYMBOL(pwm_request);
-
-
-void pwm_free(struct pwm_device *pwm)
+static inline int pwm_is_tdiv(struct s3c_chip *chip)
 {
-	mutex_lock(&pwm_lock);
-
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else
-		printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id);
-
-	mutex_unlock(&pwm_lock);
+	return clk_get_parent(chip->clk) == chip->clk_div;
 }
 
-EXPORT_SYMBOL(pwm_free);
-
 #define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0))
 #define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2))
 #define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3))
 #define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1))
 
-int pwm_enable(struct pwm_device *pwm)
+static int s3c_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct s3c_chip *s3c = to_s3c_chip(chip);
 	unsigned long flags;
 	unsigned long tcon;
 
 	local_irq_save(flags);
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_start(pwm);
+	tcon |= pwm_tcon_start(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
 
-	pwm->running = 1;
 	return 0;
 }
 
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
+static void s3c_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct s3c_chip *s3c = to_s3c_chip(chip);
 	unsigned long flags;
 	unsigned long tcon;
 
 	local_irq_save(flags);
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon &= ~pwm_tcon_start(pwm);
+	tcon &= ~pwm_tcon_start(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
-
-	pwm->running = 0;
 }
 
-EXPORT_SYMBOL(pwm_disable);
-
-static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq)
+static unsigned long pwm_calc_tin(struct s3c_chip *s3c, unsigned long freq)
 {
 	unsigned long tin_parent_rate;
 	unsigned int div;
 
-	tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div));
-	pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate);
+	tin_parent_rate = clk_get_rate(clk_get_parent(s3c->clk_div));
+	pwm_dbg(s3c, "tin parent at %lu\n", tin_parent_rate);
 
 	for (div = 2; div <= 16; div *= 2) {
 		if ((tin_parent_rate / (div << 16)) < freq)
@@ -158,8 +105,10 @@ static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq)
 
 #define NS_IN_HZ (1000000000UL)
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+		int duty_ns, int period_ns)
 {
+	struct s3c_chip *s3c = to_s3c_chip(chip);
 	unsigned long tin_rate;
 	unsigned long tin_ns;
 	unsigned long period;
@@ -178,38 +127,38 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	if (duty_ns > period_ns)
 		return -EINVAL;
 
-	if (period_ns == pwm->period_ns &&
-	    duty_ns == pwm->duty_ns)
+	if (period_ns == s3c->period_ns &&
+	    duty_ns == s3c->duty_ns)
 		return 0;
 
 	/* The TCMP and TCNT can be read without a lock, they're not
 	 * shared between the timers. */
 
-	tcmp = __raw_readl(S3C2410_TCMPB(pwm->pwm_id));
-	tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id));
+	tcmp = __raw_readl(S3C2410_TCMPB(s3c->pwm_id));
+	tcnt = __raw_readl(S3C2410_TCNTB(s3c->pwm_id));
 
 	period = NS_IN_HZ / period_ns;
 
-	pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n",
+	pwm_dbg(s3c, "duty_ns=%d, period_ns=%d (%lu)\n",
 		duty_ns, period_ns, period);
 
 	/* Check to see if we are changing the clock rate of the PWM */
 
-	if (pwm->period_ns != period_ns) {
-		if (pwm_is_tdiv(pwm)) {
-			tin_rate = pwm_calc_tin(pwm, period);
-			clk_set_rate(pwm->clk_div, tin_rate);
+	if (s3c->period_ns != period_ns) {
+		if (pwm_is_tdiv(s3c)) {
+			tin_rate = pwm_calc_tin(s3c, period);
+			clk_set_rate(s3c->clk_div, tin_rate);
 		} else
-			tin_rate = clk_get_rate(pwm->clk);
+			tin_rate = clk_get_rate(s3c->clk);
 
-		pwm->period_ns = period_ns;
+		s3c->period_ns = period_ns;
 
-		pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate);
+		pwm_dbg(s3c, "tin_rate=%lu\n", tin_rate);
 
 		tin_ns = NS_IN_HZ / tin_rate;
 		tcnt = period_ns / tin_ns;
 	} else
-		tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk);
+		tin_ns = NS_IN_HZ / clk_get_rate(s3c->clk);
 
 	/* Note, counters count down */
 
@@ -220,7 +169,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	if (tcmp == tcnt)
 		tcmp--;
 
-	pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
+	pwm_dbg(s3c, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
 
 	if (tcmp < 0)
 		tcmp = 0;
@@ -229,15 +178,15 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 
 	local_irq_save(flags);
 
-	__raw_writel(tcmp, S3C2410_TCMPB(pwm->pwm_id));
-	__raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id));
+	__raw_writel(tcmp, S3C2410_TCMPB(s3c->pwm_id));
+	__raw_writel(tcnt, S3C2410_TCNTB(s3c->pwm_id));
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_manulupdate(pwm);
-	tcon |= pwm_tcon_autoreload(pwm);
+	tcon |= pwm_tcon_manulupdate(s3c);
+	tcon |= pwm_tcon_autoreload(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
-	tcon &= ~pwm_tcon_manulupdate(pwm);
+	tcon &= ~pwm_tcon_manulupdate(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
@@ -245,24 +194,17 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	return 0;
 }
 
-EXPORT_SYMBOL(pwm_config);
-
-static int pwm_register(struct pwm_device *pwm)
-{
-	pwm->duty_ns = -1;
-	pwm->period_ns = -1;
-
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->list, &pwm_list);
-	mutex_unlock(&pwm_lock);
-
-	return 0;
-}
+static struct pwm_ops s3c_pwm_ops = {
+	.enable = s3c_pwm_enable,
+	.disable = s3c_pwm_disable,
+	.config = s3c_pwm_config,
+	.owner = THIS_MODULE,
+};
 
 static int s3c_pwm_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct pwm_device *pwm;
+	struct s3c_chip *s3c;
 	unsigned long flags;
 	unsigned long tcon;
 	unsigned int id = pdev->id;
@@ -273,83 +215,87 @@ static int s3c_pwm_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
-	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-	if (pwm == NULL) {
+	s3c = kzalloc(sizeof(*s3c), GFP_KERNEL);
+	if (s3c == NULL) {
 		dev_err(dev, "failed to allocate pwm_device\n");
 		return -ENOMEM;
 	}
 
-	pwm->pdev = pdev;
-	pwm->pwm_id = id;
-
 	/* calculate base of control bits in TCON */
-	pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;
+	s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4;
+	s3c->chip.ops = &s3c_pwm_ops;
+	s3c->chip.base = -1;
+	s3c->chip.npwm = 1;
 
-	pwm->clk = clk_get(dev, "pwm-tin");
-	if (IS_ERR(pwm->clk)) {
+	s3c->clk = clk_get(dev, "pwm-tin");
+	if (IS_ERR(s3c->clk)) {
 		dev_err(dev, "failed to get pwm tin clk\n");
-		ret = PTR_ERR(pwm->clk);
+		ret = PTR_ERR(s3c->clk);
 		goto err_alloc;
 	}
 
-	pwm->clk_div = clk_get(dev, "pwm-tdiv");
-	if (IS_ERR(pwm->clk_div)) {
+	s3c->clk_div = clk_get(dev, "pwm-tdiv");
+	if (IS_ERR(s3c->clk_div)) {
 		dev_err(dev, "failed to get pwm tdiv clk\n");
-		ret = PTR_ERR(pwm->clk_div);
+		ret = PTR_ERR(s3c->clk_div);
 		goto err_clk_tin;
 	}
 
-	clk_enable(pwm->clk);
-	clk_enable(pwm->clk_div);
+	clk_enable(s3c->clk);
+	clk_enable(s3c->clk_div);
 
 	local_irq_save(flags);
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_invert(pwm);
+	tcon |= pwm_tcon_invert(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
 
-
-	ret = pwm_register(pwm);
+	ret = pwmchip_add(&s3c->chip);
 	if (ret) {
 		dev_err(dev, "failed to register pwm\n");
 		goto err_clk_tdiv;
 	}
 
-	pwm_dbg(pwm, "config bits %02x\n",
-		(__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f);
+	pwm_dbg(s3c, "config bits %02x\n",
+		(__raw_readl(S3C2410_TCON) >> s3c->tcon_base) & 0x0f);
 
 	dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n",
-		 clk_get_rate(pwm->clk),
-		 clk_get_rate(pwm->clk_div),
-		 pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base);
+		 clk_get_rate(s3c->clk),
+		 clk_get_rate(s3c->clk_div),
+		 pwm_is_tdiv(s3c) ? "div" : "ext", s3c->tcon_base);
 
-	platform_set_drvdata(pdev, pwm);
+	platform_set_drvdata(pdev, s3c);
 	return 0;
 
  err_clk_tdiv:
-	clk_disable(pwm->clk_div);
-	clk_disable(pwm->clk);
-	clk_put(pwm->clk_div);
+	clk_disable(s3c->clk_div);
+	clk_disable(s3c->clk);
+	clk_put(s3c->clk_div);
 
  err_clk_tin:
-	clk_put(pwm->clk);
+	clk_put(s3c->clk);
 
  err_alloc:
-	kfree(pwm);
+	kfree(s3c);
 	return ret;
 }
 
 static int __devexit s3c_pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwm = platform_get_drvdata(pdev);
+	struct s3c_chip *s3c = platform_get_drvdata(pdev);
+	int err;
+
+	err = pwmchip_remove(&s3c->chip);
+	if (err < 0)
+		return err;
 
-	clk_disable(pwm->clk_div);
-	clk_disable(pwm->clk);
-	clk_put(pwm->clk_div);
-	clk_put(pwm->clk);
-	kfree(pwm);
+	clk_disable(s3c->clk_div);
+	clk_disable(s3c->clk);
+	clk_put(s3c->clk_div);
+	clk_put(s3c->clk);
+	kfree(s3c);
 
 	return 0;
 }
@@ -357,26 +303,26 @@ static int __devexit s3c_pwm_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
 {
-	struct pwm_device *pwm = platform_get_drvdata(pdev);
+	struct s3c_chip *s3c = platform_get_drvdata(pdev);
 
 	/* No one preserve these values during suspend so reset them
 	 * Otherwise driver leaves PWM unconfigured if same values
 	 * passed to pwm_config
 	 */
-	pwm->period_ns = 0;
-	pwm->duty_ns = 0;
+	s3c->period_ns = 0;
+	s3c->duty_ns = 0;
 
 	return 0;
 }
 
 static int s3c_pwm_resume(struct platform_device *pdev)
 {
-	struct pwm_device *pwm = platform_get_drvdata(pdev);
+	struct s3c_chip *s3c = platform_get_drvdata(pdev);
 	unsigned long tcon;
 
 	/* Restore invertion */
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_invert(pwm);
+	tcon |= pwm_tcon_invert(s3c);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	return 0;
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 14/17] ARM vt8500: Move vt8500 pwm driver to pwm framework
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: Mark Brown, Eric Miao, Ryan Mallon, Sascha Hauer, Colin Cross,
	Rob Herring, Lars-Peter Clausen, Alexey Charkov, Bernhard Walle,
	Matthias Kaehlcke, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	Richard Purdie,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck

From: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

Move the driver to drivers/pwm/ and convert it to use the framework.

Signed-off-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Cc: Alexey Charkov <alchark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
Changes in v5:
- adopt patch by Sascha Hauer to move vt8500 driver to PWM framework

 arch/arm/mach-vt8500/Makefile                      |    2 -
 drivers/pwm/Kconfig                                |    9 ++
 drivers/pwm/Makefile                               |    1 +
 .../mach-vt8500/pwm.c => drivers/pwm/pwm-vt8500.c  |  169 +++++++-------------
 4 files changed, 66 insertions(+), 115 deletions(-)
 rename arch/arm/mach-vt8500/pwm.c => drivers/pwm/pwm-vt8500.c (52%)

diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
index 81aedb7..8df9e4a 100644
--- a/arch/arm/mach-vt8500/Makefile
+++ b/arch/arm/mach-vt8500/Makefile
@@ -5,5 +5,3 @@ obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
 
 obj-$(CONFIG_MACH_BV07) += bv07.o
 obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
-
-obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0d9aa92..a93feff 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -55,4 +55,13 @@ config PWM_TEGRA
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-tegra.
 
+config PWM_VT8500
+	tristate "vt8500 pwm support"
+	depends on ARCH_VT8500
+	help
+	  Generic PWM framework driver for vt8500.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-vt8500.
+
 endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index a1d87b1..b7c0fcf 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
 obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
+obj-$(CONFIG_PWM_VT8500)	+= pwm-vt8500.o
diff --git a/arch/arm/mach-vt8500/pwm.c b/drivers/pwm/pwm-vt8500.c
similarity index 52%
rename from arch/arm/mach-vt8500/pwm.c
rename to drivers/pwm/pwm-vt8500.c
index 8ad825e..3db0746 100644
--- a/arch/arm/mach-vt8500/pwm.c
+++ b/drivers/pwm/pwm-vt8500.c
@@ -26,21 +26,13 @@
 
 #define VT8500_NR_PWMS 4
 
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device {
-	struct list_head	node;
-	struct platform_device	*pdev;
-
-	const char	*label;
-
-	void __iomem	*regbase;
-
-	unsigned int	use_count;
-	unsigned int	pwm_id;
+struct vt8500_chip {
+	struct pwm_chip chip;
+	void __iomem *base;
 };
 
+#define to_vt8500_chip(chip)	container_of(chip, struct vt8500_chip, chip)
+
 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
 static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
 {
@@ -53,14 +45,13 @@ static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
 			   bitmask);
 }
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+		int duty_ns, int period_ns)
 {
+	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
 	unsigned long long c;
 	unsigned long period_cycles, prescale, pv, dc;
 
-	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
-		return -EINVAL;
-
 	c = 25000000/2; /* wild guess --- need to implement clocks */
 	c = c * period_ns;
 	do_div(c, 1000000000);
@@ -80,104 +71,58 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	do_div(c, period_ns);
 	dc = c;
 
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
-	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 1));
+	writel(prescale, vt8500->base + 0x4 + (pwm->hwpwm << 4));
 
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
-	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 2));
+	writel(pv, vt8500->base + 0x8 + (pwm->hwpwm << 4));
 
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
-	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3));
+	writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4));
 
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
-	writel(5, pwm->regbase + (pwm->pwm_id << 4));
-	return 0;
-}
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
-	writel(0, pwm->regbase + (pwm->pwm_id << 4));
-}
-EXPORT_SYMBOL(pwm_disable);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int found = 0;
-
-	mutex_lock(&pwm_lock);
+	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
 
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
-	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count++;
-			pwm->label = label;
-		} else {
-			pwm = ERR_PTR(-EBUSY);
-		}
-	} else {
-		pwm = ERR_PTR(-ENOENT);
-	}
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0));
+	writel(5, vt8500->base + (pwm->hwpwm << 4));
+	return 0;
 }
-EXPORT_SYMBOL(pwm_request);
 
-void pwm_free(struct pwm_device *pwm)
+static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	mutex_lock(&pwm_lock);
-
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else {
-		pr_warning("PWM device already freed\n");
-	}
+	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
 
-	mutex_unlock(&pwm_lock);
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0));
+	writel(0, vt8500->base + (pwm->hwpwm << 4));
 }
-EXPORT_SYMBOL(pwm_free);
 
-static inline void __add_pwm(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->node, &pwm_list);
-	mutex_unlock(&pwm_lock);
-}
+static struct pwm_ops vt8500_pwm_ops = {
+	.enable = vt8500_pwm_enable,
+	.disable = vt8500_pwm_disable,
+	.config = vt8500_pwm_config,
+	.owner = THIS_MODULE,
+};
 
 static int __devinit pwm_probe(struct platform_device *pdev)
 {
-	struct pwm_device *pwms;
+	struct vt8500_chip *chip;
 	struct resource *r;
-	int ret = 0;
-	int i;
+	int ret;
 
-	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
-	if (pwms == NULL) {
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	for (i = 0; i < VT8500_NR_PWMS; i++) {
-		pwms[i].use_count = 0;
-		pwms[i].pwm_id = i;
-		pwms[i].pdev = pdev;
-	}
+	chip->chip.dev = &pdev->dev;
+	chip->chip.ops = &vt8500_pwm_ops;
+	chip->chip.base = -1;
+	chip->chip.npwm = VT8500_NR_PWMS;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (r == NULL) {
@@ -193,51 +138,49 @@ static int __devinit pwm_probe(struct platform_device *pdev)
 		goto err_free;
 	}
 
-	pwms[0].regbase = ioremap(r->start, resource_size(r));
-	if (pwms[0].regbase == NULL) {
+	chip->base = ioremap(r->start, resource_size(r));
+	if (chip->base == NULL) {
 		dev_err(&pdev->dev, "failed to ioremap() registers\n");
 		ret = -ENODEV;
 		goto err_free_mem;
 	}
 
-	for (i = 1; i < VT8500_NR_PWMS; i++)
-		pwms[i].regbase = pwms[0].regbase;
-
-	for (i = 0; i < VT8500_NR_PWMS; i++)
-		__add_pwm(&pwms[i]);
+	ret = pwmchip_add(&chip->chip);
+	if (ret < 0)
+		goto err_unmap;
 
-	platform_set_drvdata(pdev, pwms);
-	return 0;
+	platform_set_drvdata(pdev, chip);
+	return ret;
 
+err_unmap:
+	iounmap(chip->base);
 err_free_mem:
 	release_mem_region(r->start, resource_size(r));
 err_free:
-	kfree(pwms);
+	kfree(chip);
 	return ret;
 }
 
 static int __devexit pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwms;
+	struct vt8500_chip *chip;
 	struct resource *r;
-	int i;
+	int err;
 
-	pwms = platform_get_drvdata(pdev);
-	if (pwms == NULL)
+	chip = platform_get_drvdata(pdev);
+	if (chip == NULL)
 		return -ENODEV;
 
-	mutex_lock(&pwm_lock);
-
-	for (i = 0; i < VT8500_NR_PWMS; i++)
-		list_del(&pwms[i].node);
-	mutex_unlock(&pwm_lock);
+	err = pwmchip_remove(&chip->chip);
+	if (err < 0)
+		return err;
 
-	iounmap(pwms[0].regbase);
+	iounmap(chip->base);
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, resource_size(r));
 
-	kfree(pwms);
+	kfree(chip);
 	return 0;
 }
 
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 14/17] ARM vt8500: Move vt8500 pwm driver to pwm framework
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

From: Sascha Hauer <s.hauer@pengutronix.de>

Move the driver to drivers/pwm/ and convert it to use the framework.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Alexey Charkov <alchark@gmail.com>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
Changes in v5:
- adopt patch by Sascha Hauer to move vt8500 driver to PWM framework

 arch/arm/mach-vt8500/Makefile                      |    2 -
 drivers/pwm/Kconfig                                |    9 ++
 drivers/pwm/Makefile                               |    1 +
 .../mach-vt8500/pwm.c => drivers/pwm/pwm-vt8500.c  |  169 +++++++-------------
 4 files changed, 66 insertions(+), 115 deletions(-)
 rename arch/arm/mach-vt8500/pwm.c => drivers/pwm/pwm-vt8500.c (52%)

diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
index 81aedb7..8df9e4a 100644
--- a/arch/arm/mach-vt8500/Makefile
+++ b/arch/arm/mach-vt8500/Makefile
@@ -5,5 +5,3 @@ obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
 
 obj-$(CONFIG_MACH_BV07) += bv07.o
 obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
-
-obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0d9aa92..a93feff 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -55,4 +55,13 @@ config PWM_TEGRA
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-tegra.
 
+config PWM_VT8500
+	tristate "vt8500 pwm support"
+	depends on ARCH_VT8500
+	help
+	  Generic PWM framework driver for vt8500.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-vt8500.
+
 endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index a1d87b1..b7c0fcf 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
 obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
+obj-$(CONFIG_PWM_VT8500)	+= pwm-vt8500.o
diff --git a/arch/arm/mach-vt8500/pwm.c b/drivers/pwm/pwm-vt8500.c
similarity index 52%
rename from arch/arm/mach-vt8500/pwm.c
rename to drivers/pwm/pwm-vt8500.c
index 8ad825e..3db0746 100644
--- a/arch/arm/mach-vt8500/pwm.c
+++ b/drivers/pwm/pwm-vt8500.c
@@ -26,21 +26,13 @@
 
 #define VT8500_NR_PWMS 4
 
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
-
-struct pwm_device {
-	struct list_head	node;
-	struct platform_device	*pdev;
-
-	const char	*label;
-
-	void __iomem	*regbase;
-
-	unsigned int	use_count;
-	unsigned int	pwm_id;
+struct vt8500_chip {
+	struct pwm_chip chip;
+	void __iomem *base;
 };
 
+#define to_vt8500_chip(chip)	container_of(chip, struct vt8500_chip, chip)
+
 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
 static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
 {
@@ -53,14 +45,13 @@ static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
 			   bitmask);
 }
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+		int duty_ns, int period_ns)
 {
+	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
 	unsigned long long c;
 	unsigned long period_cycles, prescale, pv, dc;
 
-	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
-		return -EINVAL;
-
 	c = 25000000/2; /* wild guess --- need to implement clocks */
 	c = c * period_ns;
 	do_div(c, 1000000000);
@@ -80,104 +71,58 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	do_div(c, period_ns);
 	dc = c;
 
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
-	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 1));
+	writel(prescale, vt8500->base + 0x4 + (pwm->hwpwm << 4));
 
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
-	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 2));
+	writel(pv, vt8500->base + 0x8 + (pwm->hwpwm << 4));
 
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
-	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3));
+	writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4));
 
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
-	writel(5, pwm->regbase + (pwm->pwm_id << 4));
-	return 0;
-}
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
-	writel(0, pwm->regbase + (pwm->pwm_id << 4));
-}
-EXPORT_SYMBOL(pwm_disable);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int found = 0;
-
-	mutex_lock(&pwm_lock);
+	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
 
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
-	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count++;
-			pwm->label = label;
-		} else {
-			pwm = ERR_PTR(-EBUSY);
-		}
-	} else {
-		pwm = ERR_PTR(-ENOENT);
-	}
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0));
+	writel(5, vt8500->base + (pwm->hwpwm << 4));
+	return 0;
 }
-EXPORT_SYMBOL(pwm_request);
 
-void pwm_free(struct pwm_device *pwm)
+static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	mutex_lock(&pwm_lock);
-
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else {
-		pr_warning("PWM device already freed\n");
-	}
+	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
 
-	mutex_unlock(&pwm_lock);
+	pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0));
+	writel(0, vt8500->base + (pwm->hwpwm << 4));
 }
-EXPORT_SYMBOL(pwm_free);
 
-static inline void __add_pwm(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->node, &pwm_list);
-	mutex_unlock(&pwm_lock);
-}
+static struct pwm_ops vt8500_pwm_ops = {
+	.enable = vt8500_pwm_enable,
+	.disable = vt8500_pwm_disable,
+	.config = vt8500_pwm_config,
+	.owner = THIS_MODULE,
+};
 
 static int __devinit pwm_probe(struct platform_device *pdev)
 {
-	struct pwm_device *pwms;
+	struct vt8500_chip *chip;
 	struct resource *r;
-	int ret = 0;
-	int i;
+	int ret;
 
-	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
-	if (pwms == NULL) {
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	for (i = 0; i < VT8500_NR_PWMS; i++) {
-		pwms[i].use_count = 0;
-		pwms[i].pwm_id = i;
-		pwms[i].pdev = pdev;
-	}
+	chip->chip.dev = &pdev->dev;
+	chip->chip.ops = &vt8500_pwm_ops;
+	chip->chip.base = -1;
+	chip->chip.npwm = VT8500_NR_PWMS;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (r == NULL) {
@@ -193,51 +138,49 @@ static int __devinit pwm_probe(struct platform_device *pdev)
 		goto err_free;
 	}
 
-	pwms[0].regbase = ioremap(r->start, resource_size(r));
-	if (pwms[0].regbase == NULL) {
+	chip->base = ioremap(r->start, resource_size(r));
+	if (chip->base == NULL) {
 		dev_err(&pdev->dev, "failed to ioremap() registers\n");
 		ret = -ENODEV;
 		goto err_free_mem;
 	}
 
-	for (i = 1; i < VT8500_NR_PWMS; i++)
-		pwms[i].regbase = pwms[0].regbase;
-
-	for (i = 0; i < VT8500_NR_PWMS; i++)
-		__add_pwm(&pwms[i]);
+	ret = pwmchip_add(&chip->chip);
+	if (ret < 0)
+		goto err_unmap;
 
-	platform_set_drvdata(pdev, pwms);
-	return 0;
+	platform_set_drvdata(pdev, chip);
+	return ret;
 
+err_unmap:
+	iounmap(chip->base);
 err_free_mem:
 	release_mem_region(r->start, resource_size(r));
 err_free:
-	kfree(pwms);
+	kfree(chip);
 	return ret;
 }
 
 static int __devexit pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwms;
+	struct vt8500_chip *chip;
 	struct resource *r;
-	int i;
+	int err;
 
-	pwms = platform_get_drvdata(pdev);
-	if (pwms == NULL)
+	chip = platform_get_drvdata(pdev);
+	if (chip == NULL)
 		return -ENODEV;
 
-	mutex_lock(&pwm_lock);
-
-	for (i = 0; i < VT8500_NR_PWMS; i++)
-		list_del(&pwms[i].node);
-	mutex_unlock(&pwm_lock);
+	err = pwmchip_remove(&chip->chip);
+	if (err < 0)
+		return err;
 
-	iounmap(pwms[0].regbase);
+	iounmap(chip->base);
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, resource_size(r));
 
-	kfree(pwms);
+	kfree(chip);
 	return 0;
 }
 
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 15/17] pwm: add pwm-mxs support
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: Shawn Guo, Sascha Hauer,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Bernhard Walle

From: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Add generic PWM framework driver (DT only) for Freescale MXS.

Signed-off-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Signed-off-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
Changes in v6:
- adopt patch by Shawn Guo

 Documentation/devicetree/bindings/pwm/mxs-pwm.txt |   17 ++
 drivers/pwm/Kconfig                               |    9 +
 drivers/pwm/Makefile                              |    1 +
 drivers/pwm/pwm-mxs.c                             |  207 +++++++++++++++++++++
 4 files changed, 234 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/mxs-pwm.txt
 create mode 100644 drivers/pwm/pwm-mxs.c

diff --git a/Documentation/devicetree/bindings/pwm/mxs-pwm.txt b/Documentation/devicetree/bindings/pwm/mxs-pwm.txt
new file mode 100644
index 0000000..48ead0d
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/mxs-pwm.txt
@@ -0,0 +1,17 @@
+Freescale MXS PWM controller
+
+Required properties:
+- compatible: should be "fsl,mxs-pwm"
+- reg: physical base address and length of the controller's registers
+- #pwm-cells: should be 2.  The first cell specifies the per-chip index
+  of the PWM to use and the second cell is the duty cycle in nanoseconds.
+- fsl,pwm-number: the number of PWM devices
+
+Example:
+
+pwm: pwm@80064000 {
+	compatible = "fsl,imx28-pwm", "fsl,mxs-pwm";
+	reg = <0x80064000 2000>;
+	#pwm-cells = <2>;
+	fsl,pwm-number = <8>;
+};
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index a93feff..39bdebc 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -27,6 +27,15 @@ config PWM_IMX
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-imx.
 
+config PWM_MXS
+	tristate "Freescale MXS PWM support"
+	depends on ARCH_MXS && OF
+	help
+	  Generic PWM framework driver for Freescale MXS.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-mxs.
+
 config PWM_PXA
 	tristate "PXA PWM support"
 	depends on ARCH_PXA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index b7c0fcf..cec2500 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,6 +1,7 @@
 obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
 obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o
+obj-$(CONFIG_PWM_MXS)		+= pwm-mxs.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
 obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c
new file mode 100644
index 0000000..ebf91da
--- /dev/null
+++ b/drivers/pwm/pwm-mxs.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <mach/common.h>
+
+#define SET	0x4
+#define CLR	0x8
+#define TOG	0xc
+
+#define PWM_CTRL		0x0
+#define PWM_ACTIVE0		0x10
+#define PWM_PERIOD0		0x20
+#define  PERIOD_PERIOD(p)	((p) & 0xffff)
+#define  PERIOD_PERIOD_MAX	0x10000
+#define  PERIOD_ACTIVE_HIGH	(3 << 16)
+#define  PERIOD_INACTIVE_LOW	(2 << 18)
+#define  PERIOD_CDIV(div)	(((div) & 0x7) << 20)
+#define  PERIOD_CDIV_MAX	8
+
+struct mxs_pwm_chip {
+	struct pwm_chip chip;
+	struct device *dev;
+	struct clk *clk;
+	void __iomem *base;
+};
+
+#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
+
+static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+			  int duty_ns, int period_ns)
+{
+	struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+	int ret, div = 0;
+	unsigned int period_cycles, duty_cycles;
+	unsigned long rate;
+	unsigned long long c;
+
+	rate = clk_get_rate(mxs->clk);
+	while (1) {
+		c = rate / (1 << div);
+		c = c * period_ns;
+		do_div(c, 1000000000);
+		if (c < PERIOD_PERIOD_MAX)
+			break;
+		div++;
+		if (div > PERIOD_CDIV_MAX)
+			return -EINVAL;
+	}
+
+	period_cycles = c;
+	c *= duty_ns;
+	do_div(c, period_ns);
+	duty_cycles = c;
+
+	/*
+	 * If the PWM channel is disabled, make sure to turn on the clock
+	 * before writing the register. Otherwise, keep it enabled.
+	 */
+	if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+		ret = clk_prepare_enable(mxs->clk);
+		if (ret)
+			return ret;
+	}
+
+	writel(duty_cycles << 16,
+			mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
+	writel(PERIOD_PERIOD(period_cycles) | PERIOD_ACTIVE_HIGH |
+	       PERIOD_INACTIVE_LOW | PERIOD_CDIV(div),
+			mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
+
+	/*
+	 * If the PWM is not enabled, turn the clock off again to save power.
+	 */
+	if (!test_bit(PWMF_ENABLED, &pwm->flags))
+		clk_disable_unprepare(mxs->clk);
+
+	return 0;
+}
+
+static int mxs_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+	int ret;
+
+	ret = clk_prepare_enable(mxs->clk);
+	if (ret)
+		return ret;
+
+	writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
+
+	return 0;
+}
+
+static void mxs_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+
+	writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
+
+	clk_disable_unprepare(mxs->clk);
+}
+
+static const struct pwm_ops mxs_pwm_ops = {
+	.config = mxs_pwm_config,
+	.enable = mxs_pwm_enable,
+	.disable = mxs_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int mxs_pwm_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct mxs_pwm_chip *mxs;
+	int ret;
+
+	mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL);
+	if (!mxs)
+		return -ENOMEM;
+
+	mxs->base = of_iomap(np, 0);
+	if (!mxs->base)
+		return -EADDRNOTAVAIL;
+
+	mxs->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mxs->clk)) {
+		ret = PTR_ERR(mxs->clk);
+		goto iounmap;
+	}
+
+	mxs->chip.dev = &pdev->dev;
+	mxs->chip.ops = &mxs_pwm_ops;
+	mxs->chip.base = -1;
+	ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret);
+		goto clk_put;
+	}
+
+	ret = pwmchip_add(&mxs->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret);
+		goto clk_put;
+	}
+
+	mxs->dev = &pdev->dev;
+	platform_set_drvdata(pdev, mxs);
+
+	mxs_reset_block(mxs->base);
+
+	return 0;
+
+clk_put:
+	clk_put(mxs->clk);
+iounmap:
+	iounmap(mxs->base);
+	return ret;
+}
+
+static int __devexit mxs_pwm_remove(struct platform_device *pdev)
+{
+	struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev);
+
+	pwmchip_remove(&mxs->chip);
+	clk_put(mxs->clk);
+	iounmap(mxs->base);
+
+	return 0;
+}
+
+static struct of_device_id mxs_pwm_dt_ids[] = {
+	{ .compatible = "fsl,mxs-pwm", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_pwm_of_match);
+
+static struct platform_driver mxs_pwm_driver = {
+	.driver = {
+		.name = "mxs-pwm",
+		.of_match_table = of_match_ptr(mxs_pwm_dt_ids),
+	},
+	.probe = mxs_pwm_probe,
+	.remove = __devexit_p(mxs_pwm_remove),
+};
+module_platform_driver(mxs_pwm_driver);
+
+MODULE_ALIAS("platform:mxs-pwm");
+MODULE_AUTHOR("Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
+MODULE_DESCRIPTION("Freescale MXS PWM Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 15/17] pwm: add pwm-mxs support
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

From: Shawn Guo <shawn.guo@linaro.org>

Add generic PWM framework driver (DT only) for Freescale MXS.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
Changes in v6:
- adopt patch by Shawn Guo

 Documentation/devicetree/bindings/pwm/mxs-pwm.txt |   17 ++
 drivers/pwm/Kconfig                               |    9 +
 drivers/pwm/Makefile                              |    1 +
 drivers/pwm/pwm-mxs.c                             |  207 +++++++++++++++++++++
 4 files changed, 234 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/mxs-pwm.txt
 create mode 100644 drivers/pwm/pwm-mxs.c

diff --git a/Documentation/devicetree/bindings/pwm/mxs-pwm.txt b/Documentation/devicetree/bindings/pwm/mxs-pwm.txt
new file mode 100644
index 0000000..48ead0d
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/mxs-pwm.txt
@@ -0,0 +1,17 @@
+Freescale MXS PWM controller
+
+Required properties:
+- compatible: should be "fsl,mxs-pwm"
+- reg: physical base address and length of the controller's registers
+- #pwm-cells: should be 2.  The first cell specifies the per-chip index
+  of the PWM to use and the second cell is the duty cycle in nanoseconds.
+- fsl,pwm-number: the number of PWM devices
+
+Example:
+
+pwm: pwm at 80064000 {
+	compatible = "fsl,imx28-pwm", "fsl,mxs-pwm";
+	reg = <0x80064000 2000>;
+	#pwm-cells = <2>;
+	fsl,pwm-number = <8>;
+};
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index a93feff..39bdebc 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -27,6 +27,15 @@ config PWM_IMX
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-imx.
 
+config PWM_MXS
+	tristate "Freescale MXS PWM support"
+	depends on ARCH_MXS && OF
+	help
+	  Generic PWM framework driver for Freescale MXS.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-mxs.
+
 config PWM_PXA
 	tristate "PXA PWM support"
 	depends on ARCH_PXA
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index b7c0fcf..cec2500 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,6 +1,7 @@
 obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
 obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o
+obj-$(CONFIG_PWM_MXS)		+= pwm-mxs.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
 obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c
new file mode 100644
index 0000000..ebf91da
--- /dev/null
+++ b/drivers/pwm/pwm-mxs.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <mach/common.h>
+
+#define SET	0x4
+#define CLR	0x8
+#define TOG	0xc
+
+#define PWM_CTRL		0x0
+#define PWM_ACTIVE0		0x10
+#define PWM_PERIOD0		0x20
+#define  PERIOD_PERIOD(p)	((p) & 0xffff)
+#define  PERIOD_PERIOD_MAX	0x10000
+#define  PERIOD_ACTIVE_HIGH	(3 << 16)
+#define  PERIOD_INACTIVE_LOW	(2 << 18)
+#define  PERIOD_CDIV(div)	(((div) & 0x7) << 20)
+#define  PERIOD_CDIV_MAX	8
+
+struct mxs_pwm_chip {
+	struct pwm_chip chip;
+	struct device *dev;
+	struct clk *clk;
+	void __iomem *base;
+};
+
+#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
+
+static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+			  int duty_ns, int period_ns)
+{
+	struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+	int ret, div = 0;
+	unsigned int period_cycles, duty_cycles;
+	unsigned long rate;
+	unsigned long long c;
+
+	rate = clk_get_rate(mxs->clk);
+	while (1) {
+		c = rate / (1 << div);
+		c = c * period_ns;
+		do_div(c, 1000000000);
+		if (c < PERIOD_PERIOD_MAX)
+			break;
+		div++;
+		if (div > PERIOD_CDIV_MAX)
+			return -EINVAL;
+	}
+
+	period_cycles = c;
+	c *= duty_ns;
+	do_div(c, period_ns);
+	duty_cycles = c;
+
+	/*
+	 * If the PWM channel is disabled, make sure to turn on the clock
+	 * before writing the register. Otherwise, keep it enabled.
+	 */
+	if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+		ret = clk_prepare_enable(mxs->clk);
+		if (ret)
+			return ret;
+	}
+
+	writel(duty_cycles << 16,
+			mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
+	writel(PERIOD_PERIOD(period_cycles) | PERIOD_ACTIVE_HIGH |
+	       PERIOD_INACTIVE_LOW | PERIOD_CDIV(div),
+			mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
+
+	/*
+	 * If the PWM is not enabled, turn the clock off again to save power.
+	 */
+	if (!test_bit(PWMF_ENABLED, &pwm->flags))
+		clk_disable_unprepare(mxs->clk);
+
+	return 0;
+}
+
+static int mxs_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+	int ret;
+
+	ret = clk_prepare_enable(mxs->clk);
+	if (ret)
+		return ret;
+
+	writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
+
+	return 0;
+}
+
+static void mxs_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+
+	writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
+
+	clk_disable_unprepare(mxs->clk);
+}
+
+static const struct pwm_ops mxs_pwm_ops = {
+	.config = mxs_pwm_config,
+	.enable = mxs_pwm_enable,
+	.disable = mxs_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int mxs_pwm_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct mxs_pwm_chip *mxs;
+	int ret;
+
+	mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL);
+	if (!mxs)
+		return -ENOMEM;
+
+	mxs->base = of_iomap(np, 0);
+	if (!mxs->base)
+		return -EADDRNOTAVAIL;
+
+	mxs->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mxs->clk)) {
+		ret = PTR_ERR(mxs->clk);
+		goto iounmap;
+	}
+
+	mxs->chip.dev = &pdev->dev;
+	mxs->chip.ops = &mxs_pwm_ops;
+	mxs->chip.base = -1;
+	ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret);
+		goto clk_put;
+	}
+
+	ret = pwmchip_add(&mxs->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret);
+		goto clk_put;
+	}
+
+	mxs->dev = &pdev->dev;
+	platform_set_drvdata(pdev, mxs);
+
+	mxs_reset_block(mxs->base);
+
+	return 0;
+
+clk_put:
+	clk_put(mxs->clk);
+iounmap:
+	iounmap(mxs->base);
+	return ret;
+}
+
+static int __devexit mxs_pwm_remove(struct platform_device *pdev)
+{
+	struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev);
+
+	pwmchip_remove(&mxs->chip);
+	clk_put(mxs->clk);
+	iounmap(mxs->base);
+
+	return 0;
+}
+
+static struct of_device_id mxs_pwm_dt_ids[] = {
+	{ .compatible = "fsl,mxs-pwm", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_pwm_of_match);
+
+static struct platform_driver mxs_pwm_driver = {
+	.driver = {
+		.name = "mxs-pwm",
+		.of_match_table = of_match_ptr(mxs_pwm_dt_ids),
+	},
+	.probe = mxs_pwm_probe,
+	.remove = __devexit_p(mxs_pwm_remove),
+};
+module_platform_driver(mxs_pwm_driver);
+
+MODULE_ALIAS("platform:mxs-pwm");
+MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
+MODULE_DESCRIPTION("Freescale MXS PWM Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 16/17] pwm-backlight: Add rudimentary device tree support
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

This commit adds very basic support for device tree probing. Currently,
only a PWM and a list of distinct brightness levels can be specified.
Enabling or disabling backlight power via GPIOs is not yet supported.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
Changes in v6:
- don't pass consumer name to pwm_get() because we don't need multiple
  PWM devices
- drop num-brightness-levels property because it can be derived from
  the brightness-levels property itself

Changes in v5:
- use "pwms" fixed property name
- document brightness-levels and default-brightness-level properties
- check default-brightness-level against brightness-levels
- use new PWM API (pwm_get() and pwm_put()), legacy API (pwm_request())
  as fallback

Changes in v4:
- store a PWM requested with of_pwm_request() in the platform data

Changes in v3:
- use a list of distinct brightness levels instead of a linear range
  as discussed with Stephen Warren and Mitch Bradley

Changes in v2:
- avoid oops by keeping a reference to the platform-specific exit()
  callback

TODO:
- add fixed-regulator support for backlight enable/disable

 .../bindings/video/backlight/pwm-backlight         |   28 ++++
 drivers/video/backlight/Kconfig                    |    2 +-
 drivers/video/backlight/pwm_bl.c                   |  149 +++++++++++++++++---
 include/linux/pwm_backlight.h                      |    1 +
 4 files changed, 159 insertions(+), 21 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/video/backlight/pwm-backlight

diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
new file mode 100644
index 0000000..1e4fc72
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
@@ -0,0 +1,28 @@
+pwm-backlight bindings
+
+Required properties:
+  - compatible: "pwm-backlight"
+  - pwms: OF device-tree PWM specification (see PWM binding[0])
+  - brightness-levels: Array of distinct brightness levels. Typically these
+      are in the range from 0 to 255, but any range starting at 0 will do.
+      The actual brightness level (PWM duty cycle) will be interpolated
+      from these values. 0 means a 0% duty cycle (darkest/off), while the
+      last value in the array represents a 100% duty cycle (brightest).
+  - default-brightness-level: the default brightness level (index into the
+      array defined by the "brightness-levels" property)
+
+Optional properties:
+  - pwm-names: a list of names for the PWM devices specified in the
+               "pwms" property (see PWM binding[0])
+
+[0]: Documentation/devicetree/bindings/pwm/pwm.txt
+
+Example:
+
+	backlight {
+		compatible = "pwm-backlight";
+		pwms = <&pwm 0 5000000>;
+
+		brightness-levels = <0 4 8 16 32 64 128 255>;
+		default-brightness-level = <6>;
+	};
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 7ed9991..cd77e79 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -233,7 +233,7 @@ config BACKLIGHT_CARILLO_RANCH
 
 config BACKLIGHT_PWM
 	tristate "Generic PWM based Backlight Driver"
-	depends on HAVE_PWM
+	depends on PWM
 	help
 	  If you have a LCD backlight adjustable by PWM, say Y to enable
 	  this driver.
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 342b7d7..057389d 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -26,11 +26,13 @@ struct pwm_bl_data {
 	struct device		*dev;
 	unsigned int		period;
 	unsigned int		lth_brightness;
+	unsigned int		*levels;
 	int			(*notify)(struct device *,
 					  int brightness);
 	void			(*notify_after)(struct device *,
 					int brightness);
 	int			(*check_fb)(struct device *, struct fb_info *);
+	void			(*exit)(struct device *);
 };
 
 static int pwm_backlight_update_status(struct backlight_device *bl)
@@ -52,6 +54,11 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
 		pwm_config(pb->pwm, 0, pb->period);
 		pwm_disable(pb->pwm);
 	} else {
+		if (pb->levels) {
+			brightness = pb->levels[brightness];
+			max = pb->levels[max];
+		}
+
 		brightness = pb->lth_brightness +
 			(brightness * (pb->period - pb->lth_brightness) / max);
 		pwm_config(pb->pwm, brightness, pb->period);
@@ -83,17 +90,98 @@ static const struct backlight_ops pwm_backlight_ops = {
 	.check_fb	= pwm_backlight_check_fb,
 };
 
+#ifdef CONFIG_OF
+static int pwm_backlight_parse_dt(struct device *dev,
+				  struct platform_pwm_backlight_data *data)
+{
+	struct device_node *node = dev->of_node;
+	struct property *prop;
+	int length;
+	u32 value;
+	int ret;
+
+	if (!node)
+		return -ENODEV;
+
+	memset(data, 0, sizeof(*data));
+
+	/* determine the number of brightness levels */
+	prop = of_find_property(node, "brightness-levels", &length);
+	if (!prop)
+		return -EINVAL;
+
+	data->max_brightness = length / sizeof(u32);
+
+	/* read brightness levels from DT property */
+	if (data->max_brightness > 0) {
+		size_t size = sizeof(*data->levels) * data->max_brightness;
+
+		data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
+		if (!data->levels)
+			return -ENOMEM;
+
+		ret = of_property_read_u32_array(node, "brightness-levels",
+						 data->levels,
+						 data->max_brightness);
+		if (ret < 0)
+			return ret;
+
+		ret = of_property_read_u32(node, "default-brightness-level",
+					   &value);
+		if (ret < 0)
+			return ret;
+
+		if (value >= data->max_brightness) {
+			dev_warn(dev, "invalid default brightness level: %u, using %u\n",
+				 value, data->max_brightness - 1);
+			value = data->max_brightness - 1;
+		}
+
+		data->dft_brightness = value;
+		data->max_brightness--;
+	}
+
+	/*
+	 * TODO: Most users of this driver use a number of GPIOs to control
+	 *       backlight power. Support for specifying these needs to be
+	 *       added.
+	 */
+
+	return 0;
+}
+
+static struct of_device_id pwm_backlight_of_match[] = {
+	{ .compatible = "pwm-backlight" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
+#else
+static int pwm_backlight_parse_dt(struct device *dev,
+				  struct platform_pwm_backlight_data *data)
+{
+	return -ENODEV;
+}
+#endif
+
 static int pwm_backlight_probe(struct platform_device *pdev)
 {
-	struct backlight_properties props;
 	struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
+	struct platform_pwm_backlight_data defdata;
+	struct backlight_properties props;
 	struct backlight_device *bl;
 	struct pwm_bl_data *pb;
+	unsigned int max;
 	int ret;
 
 	if (!data) {
-		dev_err(&pdev->dev, "failed to find platform data\n");
-		return -EINVAL;
+		ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "failed to find platform data\n");
+			return ret;
+		}
+
+		data = &defdata;
 	}
 
 	if (data->init) {
@@ -109,21 +197,42 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 		goto err_alloc;
 	}
 
-	pb->period = data->pwm_period_ns;
+	if (data->levels) {
+		max = data->levels[data->max_brightness];
+		pb->levels = data->levels;
+	} else
+		max = data->max_brightness;
+
 	pb->notify = data->notify;
 	pb->notify_after = data->notify_after;
 	pb->check_fb = data->check_fb;
-	pb->lth_brightness = data->lth_brightness *
-		(data->pwm_period_ns / data->max_brightness);
+	pb->exit = data->exit;
 	pb->dev = &pdev->dev;
 
-	pb->pwm = pwm_request(data->pwm_id, "backlight");
+	pb->pwm = pwm_get(&pdev->dev, NULL);
 	if (IS_ERR(pb->pwm)) {
-		dev_err(&pdev->dev, "unable to request PWM for backlight\n");
-		ret = PTR_ERR(pb->pwm);
-		goto err_alloc;
-	} else
-		dev_dbg(&pdev->dev, "got pwm for backlight\n");
+		dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
+
+		pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
+		if (IS_ERR(pb->pwm)) {
+			dev_err(&pdev->dev, "unable to request legacy PWM\n");
+			ret = PTR_ERR(pb->pwm);
+			goto err_alloc;
+		}
+	}
+
+	dev_dbg(&pdev->dev, "got pwm for backlight\n");
+
+	/*
+	 * The DT case will set the pwm_period_ns field to 0 and store the
+	 * period, parsed from the DT, in the PWM device. For the non-DT case,
+	 * set the period from platform data.
+	 */
+	if (data->pwm_period_ns > 0)
+		pwm_set_period(pb->pwm, data->pwm_period_ns);
+
+	pb->period = pwm_get_period(pb->pwm);
+	pb->lth_brightness = data->lth_brightness * (pb->period / max);
 
 	memset(&props, 0, sizeof(struct backlight_properties));
 	props.type = BACKLIGHT_RAW;
@@ -143,7 +252,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 	return 0;
 
 err_bl:
-	pwm_free(pb->pwm);
+	pwm_put(pb->pwm);
 err_alloc:
 	if (data->exit)
 		data->exit(&pdev->dev);
@@ -152,16 +261,15 @@ err_alloc:
 
 static int pwm_backlight_remove(struct platform_device *pdev)
 {
-	struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
 	struct backlight_device *bl = platform_get_drvdata(pdev);
 	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 
 	backlight_device_unregister(bl);
 	pwm_config(pb->pwm, 0, pb->period);
 	pwm_disable(pb->pwm);
-	pwm_free(pb->pwm);
-	if (data->exit)
-		data->exit(&pdev->dev);
+	pwm_put(pb->pwm);
+	if (pb->exit)
+		pb->exit(&pdev->dev);
 	return 0;
 }
 
@@ -195,11 +303,12 @@ static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
 
 static struct platform_driver pwm_backlight_driver = {
 	.driver		= {
-		.name	= "pwm-backlight",
-		.owner	= THIS_MODULE,
+		.name		= "pwm-backlight",
+		.owner		= THIS_MODULE,
 #ifdef CONFIG_PM
-		.pm	= &pwm_backlight_pm_ops,
+		.pm		= &pwm_backlight_pm_ops,
 #endif
+		.of_match_table	= of_match_ptr(pwm_backlight_of_match),
 	},
 	.probe		= pwm_backlight_probe,
 	.remove		= pwm_backlight_remove,
diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h
index 63d2df4..56f4a86 100644
--- a/include/linux/pwm_backlight.h
+++ b/include/linux/pwm_backlight.h
@@ -12,6 +12,7 @@ struct platform_pwm_backlight_data {
 	unsigned int dft_brightness;
 	unsigned int lth_brightness;
 	unsigned int pwm_period_ns;
+	unsigned int *levels;
 	int (*init)(struct device *dev);
 	int (*notify)(struct device *dev, int brightness);
 	void (*notify_after)(struct device *dev, int brightness);
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 16/17] pwm-backlight: Add rudimentary device tree support
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

This commit adds very basic support for device tree probing. Currently,
only a PWM and a list of distinct brightness levels can be specified.
Enabling or disabling backlight power via GPIOs is not yet supported.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
Changes in v6:
- don't pass consumer name to pwm_get() because we don't need multiple
  PWM devices
- drop num-brightness-levels property because it can be derived from
  the brightness-levels property itself

Changes in v5:
- use "pwms" fixed property name
- document brightness-levels and default-brightness-level properties
- check default-brightness-level against brightness-levels
- use new PWM API (pwm_get() and pwm_put()), legacy API (pwm_request())
  as fallback

Changes in v4:
- store a PWM requested with of_pwm_request() in the platform data

Changes in v3:
- use a list of distinct brightness levels instead of a linear range
  as discussed with Stephen Warren and Mitch Bradley

Changes in v2:
- avoid oops by keeping a reference to the platform-specific exit()
  callback

TODO:
- add fixed-regulator support for backlight enable/disable

 .../bindings/video/backlight/pwm-backlight         |   28 ++++
 drivers/video/backlight/Kconfig                    |    2 +-
 drivers/video/backlight/pwm_bl.c                   |  149 +++++++++++++++++---
 include/linux/pwm_backlight.h                      |    1 +
 4 files changed, 159 insertions(+), 21 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/video/backlight/pwm-backlight

diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
new file mode 100644
index 0000000..1e4fc72
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
@@ -0,0 +1,28 @@
+pwm-backlight bindings
+
+Required properties:
+  - compatible: "pwm-backlight"
+  - pwms: OF device-tree PWM specification (see PWM binding[0])
+  - brightness-levels: Array of distinct brightness levels. Typically these
+      are in the range from 0 to 255, but any range starting at 0 will do.
+      The actual brightness level (PWM duty cycle) will be interpolated
+      from these values. 0 means a 0% duty cycle (darkest/off), while the
+      last value in the array represents a 100% duty cycle (brightest).
+  - default-brightness-level: the default brightness level (index into the
+      array defined by the "brightness-levels" property)
+
+Optional properties:
+  - pwm-names: a list of names for the PWM devices specified in the
+               "pwms" property (see PWM binding[0])
+
+[0]: Documentation/devicetree/bindings/pwm/pwm.txt
+
+Example:
+
+	backlight {
+		compatible = "pwm-backlight";
+		pwms = <&pwm 0 5000000>;
+
+		brightness-levels = <0 4 8 16 32 64 128 255>;
+		default-brightness-level = <6>;
+	};
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 7ed9991..cd77e79 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -233,7 +233,7 @@ config BACKLIGHT_CARILLO_RANCH
 
 config BACKLIGHT_PWM
 	tristate "Generic PWM based Backlight Driver"
-	depends on HAVE_PWM
+	depends on PWM
 	help
 	  If you have a LCD backlight adjustable by PWM, say Y to enable
 	  this driver.
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 342b7d7..057389d 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -26,11 +26,13 @@ struct pwm_bl_data {
 	struct device		*dev;
 	unsigned int		period;
 	unsigned int		lth_brightness;
+	unsigned int		*levels;
 	int			(*notify)(struct device *,
 					  int brightness);
 	void			(*notify_after)(struct device *,
 					int brightness);
 	int			(*check_fb)(struct device *, struct fb_info *);
+	void			(*exit)(struct device *);
 };
 
 static int pwm_backlight_update_status(struct backlight_device *bl)
@@ -52,6 +54,11 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
 		pwm_config(pb->pwm, 0, pb->period);
 		pwm_disable(pb->pwm);
 	} else {
+		if (pb->levels) {
+			brightness = pb->levels[brightness];
+			max = pb->levels[max];
+		}
+
 		brightness = pb->lth_brightness +
 			(brightness * (pb->period - pb->lth_brightness) / max);
 		pwm_config(pb->pwm, brightness, pb->period);
@@ -83,17 +90,98 @@ static const struct backlight_ops pwm_backlight_ops = {
 	.check_fb	= pwm_backlight_check_fb,
 };
 
+#ifdef CONFIG_OF
+static int pwm_backlight_parse_dt(struct device *dev,
+				  struct platform_pwm_backlight_data *data)
+{
+	struct device_node *node = dev->of_node;
+	struct property *prop;
+	int length;
+	u32 value;
+	int ret;
+
+	if (!node)
+		return -ENODEV;
+
+	memset(data, 0, sizeof(*data));
+
+	/* determine the number of brightness levels */
+	prop = of_find_property(node, "brightness-levels", &length);
+	if (!prop)
+		return -EINVAL;
+
+	data->max_brightness = length / sizeof(u32);
+
+	/* read brightness levels from DT property */
+	if (data->max_brightness > 0) {
+		size_t size = sizeof(*data->levels) * data->max_brightness;
+
+		data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
+		if (!data->levels)
+			return -ENOMEM;
+
+		ret = of_property_read_u32_array(node, "brightness-levels",
+						 data->levels,
+						 data->max_brightness);
+		if (ret < 0)
+			return ret;
+
+		ret = of_property_read_u32(node, "default-brightness-level",
+					   &value);
+		if (ret < 0)
+			return ret;
+
+		if (value >= data->max_brightness) {
+			dev_warn(dev, "invalid default brightness level: %u, using %u\n",
+				 value, data->max_brightness - 1);
+			value = data->max_brightness - 1;
+		}
+
+		data->dft_brightness = value;
+		data->max_brightness--;
+	}
+
+	/*
+	 * TODO: Most users of this driver use a number of GPIOs to control
+	 *       backlight power. Support for specifying these needs to be
+	 *       added.
+	 */
+
+	return 0;
+}
+
+static struct of_device_id pwm_backlight_of_match[] = {
+	{ .compatible = "pwm-backlight" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
+#else
+static int pwm_backlight_parse_dt(struct device *dev,
+				  struct platform_pwm_backlight_data *data)
+{
+	return -ENODEV;
+}
+#endif
+
 static int pwm_backlight_probe(struct platform_device *pdev)
 {
-	struct backlight_properties props;
 	struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
+	struct platform_pwm_backlight_data defdata;
+	struct backlight_properties props;
 	struct backlight_device *bl;
 	struct pwm_bl_data *pb;
+	unsigned int max;
 	int ret;
 
 	if (!data) {
-		dev_err(&pdev->dev, "failed to find platform data\n");
-		return -EINVAL;
+		ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "failed to find platform data\n");
+			return ret;
+		}
+
+		data = &defdata;
 	}
 
 	if (data->init) {
@@ -109,21 +197,42 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 		goto err_alloc;
 	}
 
-	pb->period = data->pwm_period_ns;
+	if (data->levels) {
+		max = data->levels[data->max_brightness];
+		pb->levels = data->levels;
+	} else
+		max = data->max_brightness;
+
 	pb->notify = data->notify;
 	pb->notify_after = data->notify_after;
 	pb->check_fb = data->check_fb;
-	pb->lth_brightness = data->lth_brightness *
-		(data->pwm_period_ns / data->max_brightness);
+	pb->exit = data->exit;
 	pb->dev = &pdev->dev;
 
-	pb->pwm = pwm_request(data->pwm_id, "backlight");
+	pb->pwm = pwm_get(&pdev->dev, NULL);
 	if (IS_ERR(pb->pwm)) {
-		dev_err(&pdev->dev, "unable to request PWM for backlight\n");
-		ret = PTR_ERR(pb->pwm);
-		goto err_alloc;
-	} else
-		dev_dbg(&pdev->dev, "got pwm for backlight\n");
+		dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
+
+		pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
+		if (IS_ERR(pb->pwm)) {
+			dev_err(&pdev->dev, "unable to request legacy PWM\n");
+			ret = PTR_ERR(pb->pwm);
+			goto err_alloc;
+		}
+	}
+
+	dev_dbg(&pdev->dev, "got pwm for backlight\n");
+
+	/*
+	 * The DT case will set the pwm_period_ns field to 0 and store the
+	 * period, parsed from the DT, in the PWM device. For the non-DT case,
+	 * set the period from platform data.
+	 */
+	if (data->pwm_period_ns > 0)
+		pwm_set_period(pb->pwm, data->pwm_period_ns);
+
+	pb->period = pwm_get_period(pb->pwm);
+	pb->lth_brightness = data->lth_brightness * (pb->period / max);
 
 	memset(&props, 0, sizeof(struct backlight_properties));
 	props.type = BACKLIGHT_RAW;
@@ -143,7 +252,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 	return 0;
 
 err_bl:
-	pwm_free(pb->pwm);
+	pwm_put(pb->pwm);
 err_alloc:
 	if (data->exit)
 		data->exit(&pdev->dev);
@@ -152,16 +261,15 @@ err_alloc:
 
 static int pwm_backlight_remove(struct platform_device *pdev)
 {
-	struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
 	struct backlight_device *bl = platform_get_drvdata(pdev);
 	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 
 	backlight_device_unregister(bl);
 	pwm_config(pb->pwm, 0, pb->period);
 	pwm_disable(pb->pwm);
-	pwm_free(pb->pwm);
-	if (data->exit)
-		data->exit(&pdev->dev);
+	pwm_put(pb->pwm);
+	if (pb->exit)
+		pb->exit(&pdev->dev);
 	return 0;
 }
 
@@ -195,11 +303,12 @@ static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
 
 static struct platform_driver pwm_backlight_driver = {
 	.driver		= {
-		.name	= "pwm-backlight",
-		.owner	= THIS_MODULE,
+		.name		= "pwm-backlight",
+		.owner		= THIS_MODULE,
 #ifdef CONFIG_PM
-		.pm	= &pwm_backlight_pm_ops,
+		.pm		= &pwm_backlight_pm_ops,
 #endif
+		.of_match_table	= of_match_ptr(pwm_backlight_of_match),
 	},
 	.probe		= pwm_backlight_probe,
 	.remove		= pwm_backlight_remove,
diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h
index 63d2df4..56f4a86 100644
--- a/include/linux/pwm_backlight.h
+++ b/include/linux/pwm_backlight.h
@@ -12,6 +12,7 @@ struct platform_pwm_backlight_data {
 	unsigned int dft_brightness;
 	unsigned int lth_brightness;
 	unsigned int pwm_period_ns;
+	unsigned int *levels;
 	int (*init)(struct device *dev);
 	int (*notify)(struct device *dev, int brightness);
 	void (*notify_after)(struct device *dev, int brightness);
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-10 15:06     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

I'm taking over the maintainership of the PWM subsystem. This commit
also adds the URLs to the gitorious project and repository as well as
any missing files related to the PWM subsystem.

Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
Note: I guess this patch in particular could use some Acked-by, maybe from
Arnd and Sascha so that people will know this happens with their blessing.

 MAINTAINERS |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 275db57..55a11ab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5420,10 +5420,16 @@ S:	Maintained
 F:	Documentation/video4linux/README.pvrusb2
 F:	drivers/media/video/pvrusb2/
 
-PWM core
-M:	Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
+PWM SUBSYSTEM
+M:	Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
 L:	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
 S:	Maintained
+W:	http://gitorious.org/linux-pwm
+T:	git git://gitorious.org/linux-pwm/linux-pwm.git
+F:	Documentation/pwm.txt
+F:	Documentation/devicetree/bindings/pwm/
+F:	include/linux/pwm.h
+F:	include/linux/of_pwm.h
 F:	drivers/pwm/
 
 PXA2xx/PXA3xx SUPPORT
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
@ 2012-04-10 15:06     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-10 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

I'm taking over the maintainership of the PWM subsystem. This commit
also adds the URLs to the gitorious project and repository as well as
any missing files related to the PWM subsystem.

Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
Note: I guess this patch in particular could use some Acked-by, maybe from
Arnd and Sascha so that people will know this happens with their blessing.

 MAINTAINERS |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 275db57..55a11ab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5420,10 +5420,16 @@ S:	Maintained
 F:	Documentation/video4linux/README.pvrusb2
 F:	drivers/media/video/pvrusb2/
 
-PWM core
-M:	Sascha Hauer <s.hauer@pengutronix.de>
+PWM SUBSYSTEM
+M:	Thierry Reding <thierry.reding@avionic-design.de>
 L:	linux-kernel at vger.kernel.org
 S:	Maintained
+W:	http://gitorious.org/linux-pwm
+T:	git git://gitorious.org/linux-pwm/linux-pwm.git
+F:	Documentation/pwm.txt
+F:	Documentation/devicetree/bindings/pwm/
+F:	include/linux/pwm.h
+F:	include/linux/of_pwm.h
 F:	drivers/pwm/
 
 PXA2xx/PXA3xx SUPPORT
-- 
1.7.9.6

^ permalink raw reply related	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
  2012-04-10 15:06     ` Thierry Reding
@ 2012-04-10 19:51         ` Arnd Bergmann
  -1 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2012-04-10 19:51 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Mark Brown, Ryan Mallon,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Colin Cross,
	Rob Herring, Lars-Peter Clausen, Richard Purdie, Bernhard Walle,
	Matthias Kaehlcke, linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao,
	Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck

On Tuesday 10 April 2012, Thierry Reding wrote:
> I'm taking over the maintainership of the PWM subsystem. This commit
> also adds the URLs to the gitorious project and repository as well as
> any missing files related to the PWM subsystem.
> 
> Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> ---
> Note: I guess this patch in particular could use some Acked-by, maybe from
> Arnd and Sascha so that people will know this happens with their blessing.

Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
@ 2012-04-10 19:51         ` Arnd Bergmann
  0 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2012-04-10 19:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Tuesday 10 April 2012, Thierry Reding wrote:
> I'm taking over the maintainership of the PWM subsystem. This commit
> also adds the URLs to the gitorious project and repository as well as
> any missing files related to the PWM subsystem.
> 
> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> ---
> Note: I guess this patch in particular could use some Acked-by, maybe from
> Arnd and Sascha so that people will know this happens with their blessing.

Acked-by: Arnd Bergmann <arnd@arndb.de>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
  2012-04-10 15:06     ` Thierry Reding
@ 2012-04-10 20:58         ` Sascha Hauer
  -1 siblings, 0 replies; 78+ messages in thread
From: Sascha Hauer @ 2012-04-10 20:58 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mitch Bradley,
	Mark Brown, Mike Frysinger, Ryan Mallon, Arnd Bergmann,
	Stephen Warren, Colin Cross, Rob Herring, Grant Likely,
	Olof Johansson, Lars-Peter Clausen, Richard Purdie,
	Bernhard Walle, Matthias Kaehlcke,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao, Shawn Guo,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck

On Tue, Apr 10, 2012 at 05:06:40PM +0200, Thierry Reding wrote:
> I'm taking over the maintainership of the PWM subsystem. This commit
> also adds the URLs to the gitorious project and repository as well as
> any missing files related to the PWM subsystem.
> 
> Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> ---
> Note: I guess this patch in particular could use some Acked-by, maybe from
> Arnd and Sascha so that people will know this happens with their blessing.

Acked-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
@ 2012-04-10 20:58         ` Sascha Hauer
  0 siblings, 0 replies; 78+ messages in thread
From: Sascha Hauer @ 2012-04-10 20:58 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 10, 2012 at 05:06:40PM +0200, Thierry Reding wrote:
> I'm taking over the maintainership of the PWM subsystem. This commit
> also adds the URLs to the gitorious project and repository as well as
> any missing files related to the PWM subsystem.
> 
> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> ---
> Note: I guess this patch in particular could use some Acked-by, maybe from
> Arnd and Sascha so that people will know this happens with their blessing.

Acked-by: Sascha Hauer <s.hauer@pengutronix.de>


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 03/17] pwm: Add debugfs interface
  2012-04-10 15:06     ` Thierry Reding
@ 2012-04-11 12:33         ` Shawn Guo
  -1 siblings, 0 replies; 78+ messages in thread
From: Shawn Guo @ 2012-04-11 12:33 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Bernhard Walle

On Tue, Apr 10, 2012 at 05:06:26PM +0200, Thierry Reding wrote:
> This commit adds a debugfs interface that can be used to list the
> current internal state of the PWM devices registered with the PWM
> framework.
> 
> Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> Reviewed-by: Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
> Reviewed-by: Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

I have done review only once :)

-- 
Regards,
Shawn

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 03/17] pwm: Add debugfs interface
@ 2012-04-11 12:33         ` Shawn Guo
  0 siblings, 0 replies; 78+ messages in thread
From: Shawn Guo @ 2012-04-11 12:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 10, 2012 at 05:06:26PM +0200, Thierry Reding wrote:
> This commit adds a debugfs interface that can be used to list the
> current internal state of the PWM devices registered with the PWM
> framework.
> 
> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Reviewed-by: Reviewed-by: Shawn Guo <shawn.guo@linaro.org>

I have done review only once :)

-- 
Regards,
Shawn

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 03/17] pwm: Add debugfs interface
  2012-04-11 12:33         ` Shawn Guo
@ 2012-04-11 12:38             ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-11 12:38 UTC (permalink / raw)
  To: Shawn Guo
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Bernhard Walle

[-- Attachment #1: Type: text/plain, Size: 628 bytes --]

* Shawn Guo wrote:
> On Tue, Apr 10, 2012 at 05:06:26PM +0200, Thierry Reding wrote:
> > This commit adds a debugfs interface that can be used to list the
> > current internal state of the PWM devices registered with the PWM
> > framework.
> > 
> > Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> > Reviewed-by: Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
> > Reviewed-by: Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> 
> I have done review only once :)

Heh, good catch! =)

Thanks,
Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 03/17] pwm: Add debugfs interface
@ 2012-04-11 12:38             ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-11 12:38 UTC (permalink / raw)
  To: linux-arm-kernel

* Shawn Guo wrote:
> On Tue, Apr 10, 2012 at 05:06:26PM +0200, Thierry Reding wrote:
> > This commit adds a debugfs interface that can be used to list the
> > current internal state of the PWM devices registered with the PWM
> > framework.
> > 
> > Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
> > Reviewed-by: Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
> 
> I have done review only once :)

Heh, good catch! =)

Thanks,
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120411/472df9a5/attachment.sig>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 04/17] pwm: Add table-based lookup for static mappings
  2012-04-10 15:06     ` Thierry Reding
@ 2012-04-11 12:38         ` Shawn Guo
  -1 siblings, 0 replies; 78+ messages in thread
From: Shawn Guo @ 2012-04-11 12:38 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Bernhard Walle

On Tue, Apr 10, 2012 at 05:06:27PM +0200, Thierry Reding wrote:
> In order to get rid of the global namespace for PWM devices, this commit
> provides an alternative method, similar to that of the regulator or
> clock frameworks, for registering a static mapping for PWM devices. This
> works by providing a table with a provider/consumer map in the board
> setup code.
> 
> With the new pwm_get() and pwm_put() functions available, usage of
> pwm_request() and pwm_free() becomes deprecated.
> 
> Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
...
> diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
> index 48f598a..bb3d2f9 100644
> --- a/Documentation/pwm.txt
> +++ b/Documentation/pwm.txt
> @@ -12,14 +12,33 @@ this kind of flexibility the generic PWM API exists.
>  Identifying PWMs
>  ----------------
>  
> -Users of the legacy PWM API use unique IDs to refer to PWM devices. One
> -goal of the new PWM framework is to get rid of this global namespace.
> +Users of the legacy PWM API use unique IDs to refer to PWM devices.
> +
> +Instead of referring to a PWM device via its unique ID, board setup code
> +should instead register a static mapping that can be used to match PWM
> +consumers to providers, as given in the following example:
> +
> +	static struct pwm_lookup board_pwm_lookup[] = {
> +		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),

PWM_LOOKUP takes 4 parameters now.  Other than that,

Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Regards,
Shawn

> +	};
> +
> +	static void __init board_init(void)
> +	{
> +		...
> +		pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup));
> +		...
> +	}

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 04/17] pwm: Add table-based lookup for static mappings
@ 2012-04-11 12:38         ` Shawn Guo
  0 siblings, 0 replies; 78+ messages in thread
From: Shawn Guo @ 2012-04-11 12:38 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 10, 2012 at 05:06:27PM +0200, Thierry Reding wrote:
> In order to get rid of the global namespace for PWM devices, this commit
> provides an alternative method, similar to that of the regulator or
> clock frameworks, for registering a static mapping for PWM devices. This
> works by providing a table with a provider/consumer map in the board
> setup code.
> 
> With the new pwm_get() and pwm_put() functions available, usage of
> pwm_request() and pwm_free() becomes deprecated.
> 
> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
...
> diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
> index 48f598a..bb3d2f9 100644
> --- a/Documentation/pwm.txt
> +++ b/Documentation/pwm.txt
> @@ -12,14 +12,33 @@ this kind of flexibility the generic PWM API exists.
>  Identifying PWMs
>  ----------------
>  
> -Users of the legacy PWM API use unique IDs to refer to PWM devices. One
> -goal of the new PWM framework is to get rid of this global namespace.
> +Users of the legacy PWM API use unique IDs to refer to PWM devices.
> +
> +Instead of referring to a PWM device via its unique ID, board setup code
> +should instead register a static mapping that can be used to match PWM
> +consumers to providers, as given in the following example:
> +
> +	static struct pwm_lookup board_pwm_lookup[] = {
> +		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),

PWM_LOOKUP takes 4 parameters now.  Other than that,

Reviewed-by: Shawn Guo <shawn.guo@linaro.org>

Regards,
Shawn

> +	};
> +
> +	static void __init board_init(void)
> +	{
> +		...
> +		pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup));
> +		...
> +	}

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 04/17] pwm: Add table-based lookup for static mappings
  2012-04-11 12:38         ` Shawn Guo
@ 2012-04-11 12:51             ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-11 12:51 UTC (permalink / raw)
  To: Shawn Guo
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Bernhard Walle

[-- Attachment #1: Type: text/plain, Size: 387 bytes --]

* Shawn Guo wrote:
> On Tue, Apr 10, 2012 at 05:06:27PM +0200, Thierry Reding wrote:
[...]
> > +	static struct pwm_lookup board_pwm_lookup[] = {
> > +		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),
> 
> PWM_LOOKUP takes 4 parameters now.  Other than that,

Right, I missed that.

> Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Thanks,
Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 04/17] pwm: Add table-based lookup for static mappings
@ 2012-04-11 12:51             ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-11 12:51 UTC (permalink / raw)
  To: linux-arm-kernel

* Shawn Guo wrote:
> On Tue, Apr 10, 2012 at 05:06:27PM +0200, Thierry Reding wrote:
[...]
> > +	static struct pwm_lookup board_pwm_lookup[] = {
> > +		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),
> 
> PWM_LOOKUP takes 4 parameters now.  Other than that,

Right, I missed that.

> Reviewed-by: Shawn Guo <shawn.guo@linaro.org>

Thanks,
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120411/4526fee1/attachment.sig>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 05/17] pwm: Add device tree support
  2012-04-10 15:06     ` Thierry Reding
@ 2012-04-11 13:02         ` Shawn Guo
  -1 siblings, 0 replies; 78+ messages in thread
From: Shawn Guo @ 2012-04-11 13:02 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Bernhard Walle

On Tue, Apr 10, 2012 at 05:06:28PM +0200, Thierry Reding wrote:
> This patch adds helpers to support device tree bindings for the generic
> PWM API. Device tree binding documentation for PWM controllers is also
> provided.
> 
> Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> ---
> Changes in v6:
> - don't override error code in of_pwm_simple_xlate()
> - no longer export of_pwm_request(), only pwm_get() should be used

Ok, I read this here ...

> +/**
> + * of_pwm_request() - request a PWM via the PWM framework
> + * @np: device node to get the PWM from
> + * @con_id: consumer name
> + *
> + * Returns the PWM device parsed from the phandle and index specified in the
> + * "pwms" property of a device tree node or a negative error-code on failure.
> + * Values parsed from the device tree are stored in the returned PWM device
> + * object.
> + *
> + * If con_id is NULL, the first PWM device listed in the "pwms" property will
> + * be requested. Otherwise the "pwm-names" property is used to do a reverse
> + * lookup of the PWM index. This also means that the "pwm-names" property
> + * becomes mandatory for devices that look up the PWM device via the con_id
> + * parameter.
> + */
> +static struct pwm_device *of_pwm_request(struct device_node *np,
> +					 const char *con_id)
> +{
...
> +}
> +EXPORT_SYMBOL(of_pwm_request);

... so this line should be removed.  Otherwise,

Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 05/17] pwm: Add device tree support
@ 2012-04-11 13:02         ` Shawn Guo
  0 siblings, 0 replies; 78+ messages in thread
From: Shawn Guo @ 2012-04-11 13:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 10, 2012 at 05:06:28PM +0200, Thierry Reding wrote:
> This patch adds helpers to support device tree bindings for the generic
> PWM API. Device tree binding documentation for PWM controllers is also
> provided.
> 
> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> ---
> Changes in v6:
> - don't override error code in of_pwm_simple_xlate()
> - no longer export of_pwm_request(), only pwm_get() should be used

Ok, I read this here ...

> +/**
> + * of_pwm_request() - request a PWM via the PWM framework
> + * @np: device node to get the PWM from
> + * @con_id: consumer name
> + *
> + * Returns the PWM device parsed from the phandle and index specified in the
> + * "pwms" property of a device tree node or a negative error-code on failure.
> + * Values parsed from the device tree are stored in the returned PWM device
> + * object.
> + *
> + * If con_id is NULL, the first PWM device listed in the "pwms" property will
> + * be requested. Otherwise the "pwm-names" property is used to do a reverse
> + * lookup of the PWM index. This also means that the "pwm-names" property
> + * becomes mandatory for devices that look up the PWM device via the con_id
> + * parameter.
> + */
> +static struct pwm_device *of_pwm_request(struct device_node *np,
> +					 const char *con_id)
> +{
...
> +}
> +EXPORT_SYMBOL(of_pwm_request);

... so this line should be removed.  Otherwise,

Reviewed-by: Shawn Guo <shawn.guo@linaro.org>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 05/17] pwm: Add device tree support
  2012-04-11 13:02         ` Shawn Guo
@ 2012-04-11 13:14             ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-11 13:14 UTC (permalink / raw)
  To: Shawn Guo
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Bernhard Walle

[-- Attachment #1: Type: text/plain, Size: 2107 bytes --]

* Shawn Guo wrote:
> On Tue, Apr 10, 2012 at 05:06:28PM +0200, Thierry Reding wrote:
> > This patch adds helpers to support device tree bindings for the generic
> > PWM API. Device tree binding documentation for PWM controllers is also
> > provided.
> > 
> > Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> > Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> > ---
> > Changes in v6:
> > - don't override error code in of_pwm_simple_xlate()
> > - no longer export of_pwm_request(), only pwm_get() should be used
> 
> Ok, I read this here ...
> 
> > +/**
> > + * of_pwm_request() - request a PWM via the PWM framework
> > + * @np: device node to get the PWM from
> > + * @con_id: consumer name
> > + *
> > + * Returns the PWM device parsed from the phandle and index specified in the
> > + * "pwms" property of a device tree node or a negative error-code on failure.
> > + * Values parsed from the device tree are stored in the returned PWM device
> > + * object.
> > + *
> > + * If con_id is NULL, the first PWM device listed in the "pwms" property will
> > + * be requested. Otherwise the "pwm-names" property is used to do a reverse
> > + * lookup of the PWM index. This also means that the "pwm-names" property
> > + * becomes mandatory for devices that look up the PWM device via the con_id
> > + * parameter.
> > + */
> > +static struct pwm_device *of_pwm_request(struct device_node *np,
> > +					 const char *con_id)
> > +{
> ...
> > +}
> > +EXPORT_SYMBOL(of_pwm_request);
> 
> ... so this line should be removed.  Otherwise,

You are absolutely right. I always thought that gcc would complain if you
tried to export a static function. Funny also that my compile tests haven't
caught this in the !OF configuration. Anyway, since of_pwm_request() is no
longer exported, I've replaced this by an IS_ENABLED(CONFIG_OF) construct
similar to the calls to of_pwmchip_add() and of_pwmchip_remove().

> Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Thanks,
Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 05/17] pwm: Add device tree support
@ 2012-04-11 13:14             ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-11 13:14 UTC (permalink / raw)
  To: linux-arm-kernel

* Shawn Guo wrote:
> On Tue, Apr 10, 2012 at 05:06:28PM +0200, Thierry Reding wrote:
> > This patch adds helpers to support device tree bindings for the generic
> > PWM API. Device tree binding documentation for PWM controllers is also
> > provided.
> > 
> > Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Acked-by: Arnd Bergmann <arnd@arndb.de>
> > ---
> > Changes in v6:
> > - don't override error code in of_pwm_simple_xlate()
> > - no longer export of_pwm_request(), only pwm_get() should be used
> 
> Ok, I read this here ...
> 
> > +/**
> > + * of_pwm_request() - request a PWM via the PWM framework
> > + * @np: device node to get the PWM from
> > + * @con_id: consumer name
> > + *
> > + * Returns the PWM device parsed from the phandle and index specified in the
> > + * "pwms" property of a device tree node or a negative error-code on failure.
> > + * Values parsed from the device tree are stored in the returned PWM device
> > + * object.
> > + *
> > + * If con_id is NULL, the first PWM device listed in the "pwms" property will
> > + * be requested. Otherwise the "pwm-names" property is used to do a reverse
> > + * lookup of the PWM index. This also means that the "pwm-names" property
> > + * becomes mandatory for devices that look up the PWM device via the con_id
> > + * parameter.
> > + */
> > +static struct pwm_device *of_pwm_request(struct device_node *np,
> > +					 const char *con_id)
> > +{
> ...
> > +}
> > +EXPORT_SYMBOL(of_pwm_request);
> 
> ... so this line should be removed.  Otherwise,

You are absolutely right. I always thought that gcc would complain if you
tried to export a static function. Funny also that my compile tests haven't
caught this in the !OF configuration. Anyway, since of_pwm_request() is no
longer exported, I've replaced this by an IS_ENABLED(CONFIG_OF) construct
similar to the calls to of_pwmchip_add() and of_pwmchip_remove().

> Reviewed-by: Shawn Guo <shawn.guo@linaro.org>

Thanks,
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120411/d29bce6d/attachment-0001.sig>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 16/17] pwm-backlight: Add rudimentary device tree support
  2012-04-10 15:06     ` Thierry Reding
@ 2012-04-11 13:28         ` Shawn Guo
  -1 siblings, 0 replies; 78+ messages in thread
From: Shawn Guo @ 2012-04-11 13:28 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Mark Brown, Ryan Mallon,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Colin Cross,
	Rob Herring, Lars-Peter Clausen, Richard Purdie, Bernhard Walle,
	Matthias Kaehlcke, linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao,
	Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck

On Tue, Apr 10, 2012 at 05:06:39PM +0200, Thierry Reding wrote:
> This commit adds very basic support for device tree probing. Currently,
> only a PWM and a list of distinct brightness levels can be specified.
> Enabling or disabling backlight power via GPIOs is not yet supported.
> 
> Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>

Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

[Tested on mach-mxs]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 16/17] pwm-backlight: Add rudimentary device tree support
@ 2012-04-11 13:28         ` Shawn Guo
  0 siblings, 0 replies; 78+ messages in thread
From: Shawn Guo @ 2012-04-11 13:28 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 10, 2012 at 05:06:39PM +0200, Thierry Reding wrote:
> This commit adds very basic support for device tree probing. Currently,
> only a PWM and a list of distinct brightness levels can be specified.
> Enabling or disabling backlight power via GPIOs is not yet supported.
> 
> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>

Reviewed-by: Shawn Guo <shawn.guo@linaro.org>

[Tested on mach-mxs]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 03/17] pwm: Add debugfs interface
  2012-04-11 12:33         ` Shawn Guo
@ 2012-04-11 15:11             ` Mark Brown
  -1 siblings, 0 replies; 78+ messages in thread
From: Mark Brown @ 2012-04-11 15:11 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Thierry Reding, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mitch Bradley, Mike Frysinger, Eric Miao, Lars-Peter Clausen,
	Ryan Mallon, Bernhard Walle

On Wed, Apr 11, 2012 at 08:33:56PM +0800, Shawn Guo wrote:
> On Tue, Apr 10, 2012 at 05:06:26PM +0200, Thierry Reding wrote:

> > Reviewed-by: Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

> I have done review only once :)

But it was a really thorough and detailed review!

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 03/17] pwm: Add debugfs interface
@ 2012-04-11 15:11             ` Mark Brown
  0 siblings, 0 replies; 78+ messages in thread
From: Mark Brown @ 2012-04-11 15:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 11, 2012 at 08:33:56PM +0800, Shawn Guo wrote:
> On Tue, Apr 10, 2012 at 05:06:26PM +0200, Thierry Reding wrote:

> > Reviewed-by: Reviewed-by: Shawn Guo <shawn.guo@linaro.org>

> I have done review only once :)

But it was a really thorough and detailed review!

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
  2012-04-10 19:51         ` Arnd Bergmann
@ 2012-04-12  7:22             ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-12  7:22 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

[-- Attachment #1: Type: text/plain, Size: 902 bytes --]

* Arnd Bergmann wrote:
> On Tuesday 10 April 2012, Thierry Reding wrote:
> > I'm taking over the maintainership of the PWM subsystem. This commit
> > also adds the URLs to the gitorious project and repository as well as
> > any missing files related to the PWM subsystem.
> > 
> > Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> > ---
> > Note: I guess this patch in particular could use some Acked-by, maybe from
> > Arnd and Sascha so that people will know this happens with their blessing.
> 
> Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>

As far as I can tell the series is in pretty good shape. I've integrated
Shawn's latest comments and I think we could get this into 3.5. Does it make
sense to take the PWM tree through Linux next before having it pulled into
mainline during the next merge window?

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
@ 2012-04-12  7:22             ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-12  7:22 UTC (permalink / raw)
  To: linux-arm-kernel

* Arnd Bergmann wrote:
> On Tuesday 10 April 2012, Thierry Reding wrote:
> > I'm taking over the maintainership of the PWM subsystem. This commit
> > also adds the URLs to the gitorious project and repository as well as
> > any missing files related to the PWM subsystem.
> > 
> > Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> > ---
> > Note: I guess this patch in particular could use some Acked-by, maybe from
> > Arnd and Sascha so that people will know this happens with their blessing.
> 
> Acked-by: Arnd Bergmann <arnd@arndb.de>

As far as I can tell the series is in pretty good shape. I've integrated
Shawn's latest comments and I think we could get this into 3.5. Does it make
sense to take the PWM tree through Linux next before having it pulled into
mainline during the next merge window?

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120412/6d83eac2/attachment.sig>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
  2012-04-12  7:22             ` Thierry Reding
@ 2012-04-12 11:34                 ` Arnd Bergmann
  -1 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2012-04-12 11:34 UTC (permalink / raw)
  To: Thierry Reding, Stephen Rothwell
  Cc: Mark Brown, Ryan Mallon,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Colin Cross,
	Rob Herring, Lars-Peter Clausen, Richard Purdie, Bernhard Walle,
	Matthias Kaehlcke, linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao,
	Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck

On Thursday 12 April 2012, Thierry Reding wrote:
>   * Arnd Bergmann wrote:
> > On Tuesday 10 April 2012, Thierry Reding wrote:
> > > I'm taking over the maintainership of the PWM subsystem. This commit
> > > also adds the URLs to the gitorious project and repository as well as
> > > any missing files related to the PWM subsystem.
> > > 
> > > Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> > > ---
> > > Note: I guess this patch in particular could use some Acked-by, maybe from
> > > Arnd and Sascha so that people will know this happens with their blessing.
> > 
> > Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> 
> As far as I can tell the series is in pretty good shape. I've integrated
> Shawn's latest comments and I think we could get this into 3.5. Does it make
> sense to take the PWM tree through Linux next before having it pulled into
> mainline during the next merge window?

Yes, that's the way to go. Just ask Stephen Rothwell to include it right
away if you are sufficiently happy with the contents and believe that they
don't break stuff.

	Arnd

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
@ 2012-04-12 11:34                 ` Arnd Bergmann
  0 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2012-04-12 11:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Thursday 12 April 2012, Thierry Reding wrote:
>   * Arnd Bergmann wrote:
> > On Tuesday 10 April 2012, Thierry Reding wrote:
> > > I'm taking over the maintainership of the PWM subsystem. This commit
> > > also adds the URLs to the gitorious project and repository as well as
> > > any missing files related to the PWM subsystem.
> > > 
> > > Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> > > ---
> > > Note: I guess this patch in particular could use some Acked-by, maybe from
> > > Arnd and Sascha so that people will know this happens with their blessing.
> > 
> > Acked-by: Arnd Bergmann <arnd@arndb.de>
> 
> As far as I can tell the series is in pretty good shape. I've integrated
> Shawn's latest comments and I think we could get this into 3.5. Does it make
> sense to take the PWM tree through Linux next before having it pulled into
> mainline during the next merge window?

Yes, that's the way to go. Just ask Stephen Rothwell to include it right
away if you are sufficiently happy with the contents and believe that they
don't break stuff.

	Arnd

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
  2012-04-12 11:34                 ` Arnd Bergmann
@ 2012-04-12 12:27                     ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-12 12:27 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Stephen Rothwell, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

[-- Attachment #1: Type: text/plain, Size: 1413 bytes --]

* Arnd Bergmann wrote:
> On Thursday 12 April 2012, Thierry Reding wrote:
> >   * Arnd Bergmann wrote:
> > > On Tuesday 10 April 2012, Thierry Reding wrote:
> > > > I'm taking over the maintainership of the PWM subsystem. This commit
> > > > also adds the URLs to the gitorious project and repository as well as
> > > > any missing files related to the PWM subsystem.
> > > > 
> > > > Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> > > > ---
> > > > Note: I guess this patch in particular could use some Acked-by, maybe from
> > > > Arnd and Sascha so that people will know this happens with their blessing.
> > > 
> > > Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> > 
> > As far as I can tell the series is in pretty good shape. I've integrated
> > Shawn's latest comments and I think we could get this into 3.5. Does it make
> > sense to take the PWM tree through Linux next before having it pulled into
> > mainline during the next merge window?
> 
> Yes, that's the way to go. Just ask Stephen Rothwell to include it right
> away if you are sufficiently happy with the contents and believe that they
> don't break stuff.

I just saw that there's been some discussion to remove IS_ENABLED() and
replace it by something else. So maybe I should wait another day or two
until those changes settle in next.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
@ 2012-04-12 12:27                     ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-12 12:27 UTC (permalink / raw)
  To: linux-arm-kernel

* Arnd Bergmann wrote:
> On Thursday 12 April 2012, Thierry Reding wrote:
> >   * Arnd Bergmann wrote:
> > > On Tuesday 10 April 2012, Thierry Reding wrote:
> > > > I'm taking over the maintainership of the PWM subsystem. This commit
> > > > also adds the URLs to the gitorious project and repository as well as
> > > > any missing files related to the PWM subsystem.
> > > > 
> > > > Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> > > > ---
> > > > Note: I guess this patch in particular could use some Acked-by, maybe from
> > > > Arnd and Sascha so that people will know this happens with their blessing.
> > > 
> > > Acked-by: Arnd Bergmann <arnd@arndb.de>
> > 
> > As far as I can tell the series is in pretty good shape. I've integrated
> > Shawn's latest comments and I think we could get this into 3.5. Does it make
> > sense to take the PWM tree through Linux next before having it pulled into
> > mainline during the next merge window?
> 
> Yes, that's the way to go. Just ask Stephen Rothwell to include it right
> away if you are sufficiently happy with the contents and believe that they
> don't break stuff.

I just saw that there's been some discussion to remove IS_ENABLED() and
replace it by something else. So maybe I should wait another day or two
until those changes settle in next.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120412/b6f5fb1c/attachment.sig>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 04/17] pwm: Add table-based lookup for static mappings
  2012-04-10 15:06     ` Thierry Reding
@ 2012-04-13  9:47         ` Mark Brown
  -1 siblings, 0 replies; 78+ messages in thread
From: Mark Brown @ 2012-04-13  9:47 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mitch Bradley, Mike Frysinger, Eric Miao, Lars-Peter Clausen,
	Ryan Mallon, Shawn Guo, Bernhard Walle

[-- Attachment #1: Type: text/plain, Size: 598 bytes --]

On Tue, Apr 10, 2012 at 05:06:27PM +0200, Thierry Reding wrote:
> In order to get rid of the global namespace for PWM devices, this commit
> provides an alternative method, similar to that of the regulator or
> clock frameworks, for registering a static mapping for PWM devices. This
> works by providing a table with a provider/consumer map in the board
> setup code.
> 
> With the new pwm_get() and pwm_put() functions available, usage of
> pwm_request() and pwm_free() becomes deprecated.

Reviwed-by: Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 04/17] pwm: Add table-based lookup for static mappings
@ 2012-04-13  9:47         ` Mark Brown
  0 siblings, 0 replies; 78+ messages in thread
From: Mark Brown @ 2012-04-13  9:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 10, 2012 at 05:06:27PM +0200, Thierry Reding wrote:
> In order to get rid of the global namespace for PWM devices, this commit
> provides an alternative method, similar to that of the regulator or
> clock frameworks, for registering a static mapping for PWM devices. This
> works by providing a table with a provider/consumer map in the board
> setup code.
> 
> With the new pwm_get() and pwm_put() functions available, usage of
> pwm_request() and pwm_free() becomes deprecated.

Reviwed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120413/b9114ebf/attachment.sig>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 16/17] pwm-backlight: Add rudimentary device tree support
  2012-04-10 15:06     ` Thierry Reding
@ 2012-04-13  9:48         ` Mark Brown
  -1 siblings, 0 replies; 78+ messages in thread
From: Mark Brown @ 2012-04-13  9:48 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mitch Bradley, Mike Frysinger, Eric Miao, Lars-Peter Clausen,
	Ryan Mallon, Shawn Guo, Bernhard Walle

[-- Attachment #1: Type: text/plain, Size: 378 bytes --]

On Tue, Apr 10, 2012 at 05:06:39PM +0200, Thierry Reding wrote:
> This commit adds very basic support for device tree probing. Currently,
> only a PWM and a list of distinct brightness levels can be specified.
> Enabling or disabling backlight power via GPIOs is not yet supported.

Reviewed-by: Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 16/17] pwm-backlight: Add rudimentary device tree support
@ 2012-04-13  9:48         ` Mark Brown
  0 siblings, 0 replies; 78+ messages in thread
From: Mark Brown @ 2012-04-13  9:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 10, 2012 at 05:06:39PM +0200, Thierry Reding wrote:
> This commit adds very basic support for device tree probing. Currently,
> only a PWM and a list of distinct brightness levels can be specified.
> Enabling or disabling backlight power via GPIOs is not yet supported.

Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120413/612fc777/attachment.sig>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
  2012-04-12 12:27                     ` Thierry Reding
@ 2012-04-16  5:50                         ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-16  5:50 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Stephen Rothwell, Mark Brown, Ryan Mallon,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Colin Cross,
	Rob Herring, Lars-Peter Clausen, Richard Purdie, Bernhard Walle,
	Matthias Kaehlcke, linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao,
	Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck


[-- Attachment #1.1: Type: text/plain, Size: 1895 bytes --]

* Thierry Reding wrote:
> * Arnd Bergmann wrote:
> > On Thursday 12 April 2012, Thierry Reding wrote:
> > >   * Arnd Bergmann wrote:
> > > > On Tuesday 10 April 2012, Thierry Reding wrote:
> > > > > I'm taking over the maintainership of the PWM subsystem. This commit
> > > > > also adds the URLs to the gitorious project and repository as well as
> > > > > any missing files related to the PWM subsystem.
> > > > > 
> > > > > Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> > > > > ---
> > > > > Note: I guess this patch in particular could use some Acked-by, maybe from
> > > > > Arnd and Sascha so that people will know this happens with their blessing.
> > > > 
> > > > Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> > > 
> > > As far as I can tell the series is in pretty good shape. I've integrated
> > > Shawn's latest comments and I think we could get this into 3.5. Does it make
> > > sense to take the PWM tree through Linux next before having it pulled into
> > > mainline during the next merge window?
> > 
> > Yes, that's the way to go. Just ask Stephen Rothwell to include it right
> > away if you are sufficiently happy with the contents and believe that they
> > don't break stuff.
> 
> I just saw that there's been some discussion to remove IS_ENABLED() and
> replace it by something else. So maybe I should wait another day or two
> until those changes settle in next.

Seems like this issue is resolved, but something else came up. In the !OF
builds, of_pwm_request() no longer builds because of_property_match_string()
and of_parse_phandle_with_args() have no dummies for !OF. I've posted patches
for these on Friday and they need to go in before the PWM subsystem. I could
also revert the IS_ENABLED() parts and go back to a #ifdef CONFIG_OF but I'd
rather not.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 198 bytes --]

[-- Attachment #2: Type: text/plain, Size: 192 bytes --]

_______________________________________________
devicetree-discuss mailing list
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
https://lists.ozlabs.org/listinfo/devicetree-discuss

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
@ 2012-04-16  5:50                         ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-04-16  5:50 UTC (permalink / raw)
  To: linux-arm-kernel

* Thierry Reding wrote:
> * Arnd Bergmann wrote:
> > On Thursday 12 April 2012, Thierry Reding wrote:
> > >   * Arnd Bergmann wrote:
> > > > On Tuesday 10 April 2012, Thierry Reding wrote:
> > > > > I'm taking over the maintainership of the PWM subsystem. This commit
> > > > > also adds the URLs to the gitorious project and repository as well as
> > > > > any missing files related to the PWM subsystem.
> > > > > 
> > > > > Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> > > > > ---
> > > > > Note: I guess this patch in particular could use some Acked-by, maybe from
> > > > > Arnd and Sascha so that people will know this happens with their blessing.
> > > > 
> > > > Acked-by: Arnd Bergmann <arnd@arndb.de>
> > > 
> > > As far as I can tell the series is in pretty good shape. I've integrated
> > > Shawn's latest comments and I think we could get this into 3.5. Does it make
> > > sense to take the PWM tree through Linux next before having it pulled into
> > > mainline during the next merge window?
> > 
> > Yes, that's the way to go. Just ask Stephen Rothwell to include it right
> > away if you are sufficiently happy with the contents and believe that they
> > don't break stuff.
> 
> I just saw that there's been some discussion to remove IS_ENABLED() and
> replace it by something else. So maybe I should wait another day or two
> until those changes settle in next.

Seems like this issue is resolved, but something else came up. In the !OF
builds, of_pwm_request() no longer builds because of_property_match_string()
and of_parse_phandle_with_args() have no dummies for !OF. I've posted patches
for these on Friday and they need to go in before the PWM subsystem. I could
also revert the IS_ENABLED() parts and go back to a #ifdef CONFIG_OF but I'd
rather not.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120416/36702ea1/attachment-0001.sig>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
  2012-04-16  5:50                         ` Thierry Reding
@ 2012-04-16 15:02                             ` Arnd Bergmann
  -1 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2012-04-16 15:02 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Stephen Rothwell, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Grant Likely,
	Colin Cross, Olof Johansson, Stephen Warren, Richard Purdie,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Shawn Guo, Bernhard Walle

On Monday 16 April 2012, Thierry Reding wrote:
> Seems like this issue is resolved, but something else came up. In the !OF
> builds, of_pwm_request() no longer builds because of_property_match_string()
> and of_parse_phandle_with_args() have no dummies for !OF. I've posted patches
> for these on Friday and they need to go in before the PWM subsystem. I could
> also revert the IS_ENABLED() parts and go back to a #ifdef CONFIG_OF but I'd
> rather not.
> 

Agreed, using IS_ENABLED from C code is much nicer than #ifdef because
we can be sure that all code actually builds.

	Arnd

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem
@ 2012-04-16 15:02                             ` Arnd Bergmann
  0 siblings, 0 replies; 78+ messages in thread
From: Arnd Bergmann @ 2012-04-16 15:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 16 April 2012, Thierry Reding wrote:
> Seems like this issue is resolved, but something else came up. In the !OF
> builds, of_pwm_request() no longer builds because of_property_match_string()
> and of_parse_phandle_with_args() have no dummies for !OF. I've posted patches
> for these on Friday and they need to go in before the PWM subsystem. I could
> also revert the IS_ENABLED() parts and go back to a #ifdef CONFIG_OF but I'd
> rather not.
> 

Agreed, using IS_ENABLED from C code is much nicer than #ifdef because
we can be sure that all code actually builds.

	Arnd

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 00/17] Add PWM framework and device tree support
  2012-04-10 15:06 ` Thierry Reding
@ 2012-04-29 15:40   ` Eric Bénard
  -1 siblings, 0 replies; 78+ messages in thread
From: Eric Bénard @ 2012-04-29 15:40 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Lars-Peter Clausen, Mike Frysinger, Ryan Mallon, Arnd Bergmann,
	Stephen Warren, linux-tegra, devicetree-discuss, Mark Brown,
	Matthias Kaehlcke, Rob Herring, Grant Likely, Olof Johansson,
	Bernhard Walle, Richard Purdie, Colin Cross, Mitch Bradley,
	Eric Miao, Shawn Guo, Sascha Hauer, linux-arm-kernel,
	Kurt Van Dijck

Hi Thierry,

Le Tue, 10 Apr 2012 17:06:23 +0200,
Thierry Reding <thierry.reding@avionic-design.de> a écrit :

> This patch series adds very rudimentary device-tree support for PWM
> devices. With all of these patches applied (plus one board-specific
> patch that is not included), I'm able to control the backlight on the
> device I'm working on using the sysfs interface provided by the pwm-bl
> driver and the backlight class.
> 
> This series is based on Sascha Hauer's series of patches[0] to add a
> generic PWM framework. The first patch in this series is taken from
> Sascha's branch, while the second patch enables each PWM chip to provide
> multiple PWM devices (the Blackfin and PXA drivers have been ported to
> the framework for reference). Currently a global namespace is still
> provided to keep backwards-compatibility with the legacy PWM API. In
> order to achieve this, the number of global PWM devices is limited to
> 1024. However, patch 2 introduces per-chip indexing of PWM devices in
> the core and patch 4 adds an API to request a PWM device using a lookup
> table registered by board setup code. They are supposed to eventually
> replace pwm_request() and allow to get rid of the global namespace. The
> device tree support code does not use the global namespace.
> 
> Patch 3 adds a debugfs interface.
> 
> Patch 5 adds some code to lookup a PWM chip given its device-tree
> handle. This code will be used later on by the pwm-bl driver to find the
> PWM device that it should be using. If the no corresponding PWM chip is
> available yet, the code returns EPROBE_DEFER to have users automatically
> probed again later when the PWM provider may have become available.
> Device tree binding documentation is also provided.
> 
> Patch 6 was taken from the Chromium tree and is required to provide
> proper clocking of the Tegra2 PWFM controller. All Chromium-specific
> tags have been removed from the commit message. Some cleanup of the
> clock registration for Tegra is done in patch 7 because a subsequent
> patch will instantiate one PWFM controller device instead of four.
> 
> Patch 8 adds a generic PWM framework driver for the Tegra2 PWFM
> controller. The code is taken from the Chromium tree with some
> adjustments to integrate it with the PWM framework. Device tree based
> probing of the driver is implemented in patch 9.
> 
> Patches 10 and 11 are ports of the Blackfin PWM and the PXA PWM drivers
> to the PWM framework. These are only compile-tested as I do not have any
> hardware to test them on.
> 
> Patches 12, 13 and 14 move the i.MX, Samsung and VT8500 drivers over to
> the framework. These are originally by Sascha Hauer and were updated to
> take advantage of the multiple PWM devices per chip feature. Patch 15
> adds support for Freescale MXS by Shawn Guo.
> 
> Patch 16 implements DT-based probing in the pwm-backlight driver. Note
> that this code only handles the "pwms" property (by looking up the PWM
> device via the new PWM DT binding). Switching power to the backlight via
> GPIOs is not supported yet. The DT binding also deviates from the
> platform data in that it requires a list of brightness levels to be
> specified instead of assuming a linearily spaced range from 0 to a given
> maximum brightness.
> 
> Finally, patch 17 makes me the new maintainer of the PWM subsystem.
> 
> The whole series is based on the linux-next tree from 20120405. I think
> I've addressed all of the concerns raised in the first five versions. I
> have also pushed this version of the series to the PWM subsystem
> repository[1].
> 
> Thierry
> 
> [0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
> [1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6
> 
> Sascha Hauer (4):
>   pwm: Add PWM framework support
>   ARM i.MX: Move i.MX pwm driver to pwm framework
>   ARM Samsung: Move s3c pwm driver to pwm framework
>   ARM vt8500: Move vt8500 pwm driver to pwm framework
> 
> Shawn Guo (1):
>   pwm: add pwm-mxs support
> 
> Simon Que (1):
>   ARM: tegra: Fix PWM clock programming
> 
> Thierry Reding (11):
>   pwm: Allow chips to support multiple PWMs
>   pwm: Add debugfs interface
>   pwm: Add table-based lookup for static mappings
>   pwm: Add device tree support
>   ARM: tegra: Provide clock for only one PWM controller
>   pwm: Add NVIDIA Tegra SoC support
>   pwm: tegra: Add device tree support
>   pwm: Move Blackfin PWM driver to PWM framework
>   pwm: Move PXA PWM driver to PWM framework
>   pwm-backlight: Add rudimentary device tree support
>   pwm: Take over maintainership of the PWM subsystem
> 
for pwm-core & pwm-imx (with the 3 patches I sent a few minutes ago) :
Tested-by: Eric Bénard <eric@eukrea.com>

Thanks !

Eric

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 00/17] Add PWM framework and device tree support
@ 2012-04-29 15:40   ` Eric Bénard
  0 siblings, 0 replies; 78+ messages in thread
From: Eric Bénard @ 2012-04-29 15:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Thierry,

Le Tue, 10 Apr 2012 17:06:23 +0200,
Thierry Reding <thierry.reding@avionic-design.de> a ?crit :

> This patch series adds very rudimentary device-tree support for PWM
> devices. With all of these patches applied (plus one board-specific
> patch that is not included), I'm able to control the backlight on the
> device I'm working on using the sysfs interface provided by the pwm-bl
> driver and the backlight class.
> 
> This series is based on Sascha Hauer's series of patches[0] to add a
> generic PWM framework. The first patch in this series is taken from
> Sascha's branch, while the second patch enables each PWM chip to provide
> multiple PWM devices (the Blackfin and PXA drivers have been ported to
> the framework for reference). Currently a global namespace is still
> provided to keep backwards-compatibility with the legacy PWM API. In
> order to achieve this, the number of global PWM devices is limited to
> 1024. However, patch 2 introduces per-chip indexing of PWM devices in
> the core and patch 4 adds an API to request a PWM device using a lookup
> table registered by board setup code. They are supposed to eventually
> replace pwm_request() and allow to get rid of the global namespace. The
> device tree support code does not use the global namespace.
> 
> Patch 3 adds a debugfs interface.
> 
> Patch 5 adds some code to lookup a PWM chip given its device-tree
> handle. This code will be used later on by the pwm-bl driver to find the
> PWM device that it should be using. If the no corresponding PWM chip is
> available yet, the code returns EPROBE_DEFER to have users automatically
> probed again later when the PWM provider may have become available.
> Device tree binding documentation is also provided.
> 
> Patch 6 was taken from the Chromium tree and is required to provide
> proper clocking of the Tegra2 PWFM controller. All Chromium-specific
> tags have been removed from the commit message. Some cleanup of the
> clock registration for Tegra is done in patch 7 because a subsequent
> patch will instantiate one PWFM controller device instead of four.
> 
> Patch 8 adds a generic PWM framework driver for the Tegra2 PWFM
> controller. The code is taken from the Chromium tree with some
> adjustments to integrate it with the PWM framework. Device tree based
> probing of the driver is implemented in patch 9.
> 
> Patches 10 and 11 are ports of the Blackfin PWM and the PXA PWM drivers
> to the PWM framework. These are only compile-tested as I do not have any
> hardware to test them on.
> 
> Patches 12, 13 and 14 move the i.MX, Samsung and VT8500 drivers over to
> the framework. These are originally by Sascha Hauer and were updated to
> take advantage of the multiple PWM devices per chip feature. Patch 15
> adds support for Freescale MXS by Shawn Guo.
> 
> Patch 16 implements DT-based probing in the pwm-backlight driver. Note
> that this code only handles the "pwms" property (by looking up the PWM
> device via the new PWM DT binding). Switching power to the backlight via
> GPIOs is not supported yet. The DT binding also deviates from the
> platform data in that it requires a list of brightness levels to be
> specified instead of assuming a linearily spaced range from 0 to a given
> maximum brightness.
> 
> Finally, patch 17 makes me the new maintainer of the PWM subsystem.
> 
> The whole series is based on the linux-next tree from 20120405. I think
> I've addressed all of the concerns raised in the first five versions. I
> have also pushed this version of the series to the PWM subsystem
> repository[1].
> 
> Thierry
> 
> [0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
> [1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6
> 
> Sascha Hauer (4):
>   pwm: Add PWM framework support
>   ARM i.MX: Move i.MX pwm driver to pwm framework
>   ARM Samsung: Move s3c pwm driver to pwm framework
>   ARM vt8500: Move vt8500 pwm driver to pwm framework
> 
> Shawn Guo (1):
>   pwm: add pwm-mxs support
> 
> Simon Que (1):
>   ARM: tegra: Fix PWM clock programming
> 
> Thierry Reding (11):
>   pwm: Allow chips to support multiple PWMs
>   pwm: Add debugfs interface
>   pwm: Add table-based lookup for static mappings
>   pwm: Add device tree support
>   ARM: tegra: Provide clock for only one PWM controller
>   pwm: Add NVIDIA Tegra SoC support
>   pwm: tegra: Add device tree support
>   pwm: Move Blackfin PWM driver to PWM framework
>   pwm: Move PXA PWM driver to PWM framework
>   pwm-backlight: Add rudimentary device tree support
>   pwm: Take over maintainership of the PWM subsystem
> 
for pwm-core & pwm-imx (with the 3 patches I sent a few minutes ago) :
Tested-by: Eric B?nard <eric@eukrea.com>

Thanks !

Eric

^ permalink raw reply	[flat|nested] 78+ messages in thread

* RE: [PATCH v6 00/17] Add PWM framework and device tree support
  2012-04-10 15:06 ` Thierry Reding
@ 2012-06-14 12:47     ` Hebbar, Gururaja
  -1 siblings, 0 replies; 78+ messages in thread
From: Hebbar, Gururaja @ 2012-06-14 12:47 UTC (permalink / raw)
  To: Thierry Reding, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: Mitch Bradley, Mark Brown, Mike Frysinger, Ryan Mallon,
	Arnd Bergmann, Stephen Warren, Sascha Hauer, Colin Cross,
	Rob Herring, Grant Likely, Olof Johansson, Lars-Peter Clausen,
	Richard Purdie, Bernhard Walle, Matthias Kaehlcke,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao, Shawn Guo,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck, Philip, Avinash

On Tue, Apr 10, 2012 at 20:36:23, Thierry Reding wrote:
> This patch series adds very rudimentary device-tree support for PWM
> devices. With all of these patches applied (plus one board-specific
> patch that is not included), I'm able to control the backlight on the
> device I'm working on using the sysfs interface provided by the pwm-bl
> driver and the backlight class.
> 
> This series is based on Sascha Hauer's series of patches[0] to add a
> generic PWM framework. The first patch in this series is taken from
> Sascha's branch, while the second patch enables each PWM chip to provide
> multiple PWM devices (the Blackfin and PXA drivers have been ported to
> the framework for reference). Currently a global namespace is still
> provided to keep backwards-compatibility with the legacy PWM API. In
> order to achieve this, the number of global PWM devices is limited to
> 1024. However, patch 2 introduces per-chip indexing of PWM devices in
> the core and patch 4 adds an API to request a PWM device using a lookup
> table registered by board setup code. They are supposed to eventually
> replace pwm_request() and allow to get rid of the global namespace. The
> device tree support code does not use the global namespace.
> 
..snip..
..snip..

> 
> The whole series is based on the linux-next tree from 20120405. I think
> I've addressed all of the concerns raised in the first five versions. I
> have also pushed this version of the series to the PWM subsystem
> repository[1].
> 
> Thierry
> 
> [0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
> [1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6
> 
> Sascha Hauer (4):
>   pwm: Add PWM framework support
>   ARM i.MX: Move i.MX pwm driver to pwm framework
>   ARM Samsung: Move s3c pwm driver to pwm framework
>   ARM vt8500: Move vt8500 pwm driver to pwm framework
> 
> Shawn Guo (1):
>   pwm: add pwm-mxs support
> 
> Simon Que (1):
>   ARM: tegra: Fix PWM clock programming
> 
> Thierry Reding (11):
>   pwm: Allow chips to support multiple PWMs
>   pwm: Add debugfs interface
>   pwm: Add table-based lookup for static mappings
>   pwm: Add device tree support
>   ARM: tegra: Provide clock for only one PWM controller
>   pwm: Add NVIDIA Tegra SoC support
>   pwm: tegra: Add device tree support
>   pwm: Move Blackfin PWM driver to PWM framework
>   pwm: Move PXA PWM driver to PWM framework
>   pwm-backlight: Add rudimentary device tree support
>   pwm: Take over maintainership of the PWM subsystem
> 

I am planning to add PWM support in Linux for TI AM335x SOC. 
I am taking your new PWM framework as reference.

I see a lot of ack's for the patches. I see that you are maintaining these patches in a separate repo.
However, I couldn't find any of these 
patches in the mainline kernel (including recent 3.5-rc2). 
Do you have any plans for pushing this again to mainline kernel. 


Regards, 
Gururaja

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 00/17] Add PWM framework and device tree support
@ 2012-06-14 12:47     ` Hebbar, Gururaja
  0 siblings, 0 replies; 78+ messages in thread
From: Hebbar, Gururaja @ 2012-06-14 12:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 10, 2012 at 20:36:23, Thierry Reding wrote:
> This patch series adds very rudimentary device-tree support for PWM
> devices. With all of these patches applied (plus one board-specific
> patch that is not included), I'm able to control the backlight on the
> device I'm working on using the sysfs interface provided by the pwm-bl
> driver and the backlight class.
> 
> This series is based on Sascha Hauer's series of patches[0] to add a
> generic PWM framework. The first patch in this series is taken from
> Sascha's branch, while the second patch enables each PWM chip to provide
> multiple PWM devices (the Blackfin and PXA drivers have been ported to
> the framework for reference). Currently a global namespace is still
> provided to keep backwards-compatibility with the legacy PWM API. In
> order to achieve this, the number of global PWM devices is limited to
> 1024. However, patch 2 introduces per-chip indexing of PWM devices in
> the core and patch 4 adds an API to request a PWM device using a lookup
> table registered by board setup code. They are supposed to eventually
> replace pwm_request() and allow to get rid of the global namespace. The
> device tree support code does not use the global namespace.
> 
..snip..
..snip..

> 
> The whole series is based on the linux-next tree from 20120405. I think
> I've addressed all of the concerns raised in the first five versions. I
> have also pushed this version of the series to the PWM subsystem
> repository[1].
> 
> Thierry
> 
> [0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
> [1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6
> 
> Sascha Hauer (4):
>   pwm: Add PWM framework support
>   ARM i.MX: Move i.MX pwm driver to pwm framework
>   ARM Samsung: Move s3c pwm driver to pwm framework
>   ARM vt8500: Move vt8500 pwm driver to pwm framework
> 
> Shawn Guo (1):
>   pwm: add pwm-mxs support
> 
> Simon Que (1):
>   ARM: tegra: Fix PWM clock programming
> 
> Thierry Reding (11):
>   pwm: Allow chips to support multiple PWMs
>   pwm: Add debugfs interface
>   pwm: Add table-based lookup for static mappings
>   pwm: Add device tree support
>   ARM: tegra: Provide clock for only one PWM controller
>   pwm: Add NVIDIA Tegra SoC support
>   pwm: tegra: Add device tree support
>   pwm: Move Blackfin PWM driver to PWM framework
>   pwm: Move PXA PWM driver to PWM framework
>   pwm-backlight: Add rudimentary device tree support
>   pwm: Take over maintainership of the PWM subsystem
> 

I am planning to add PWM support in Linux for TI AM335x SOC. 
I am taking your new PWM framework as reference.

I see a lot of ack's for the patches. I see that you are maintaining these patches in a separate repo.
However, I couldn't find any of these 
patches in the mainline kernel (including recent 3.5-rc2). 
Do you have any plans for pushing this again to mainline kernel. 


Regards, 
Gururaja

^ permalink raw reply	[flat|nested] 78+ messages in thread

* Re: [PATCH v6 00/17] Add PWM framework and device tree support
  2012-06-14 12:47     ` Hebbar, Gururaja
@ 2012-06-14 12:53         ` Thierry Reding
  -1 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-06-14 12:53 UTC (permalink / raw)
  To: Hebbar, Gururaja
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mitch Bradley,
	Mark Brown, Mike Frysinger, Ryan Mallon, Arnd Bergmann,
	Stephen Warren, Sascha Hauer, Colin Cross, Rob Herring,
	Grant Likely, Olof Johansson, Lars-Peter Clausen, Richard Purdie,
	Bernhard Walle, Matthias Kaehlcke,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao, Shawn Guo,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck

[-- Attachment #1: Type: text/plain, Size: 3315 bytes --]

On Thu, Jun 14, 2012 at 12:47:13PM +0000, Hebbar, Gururaja wrote:
> On Tue, Apr 10, 2012 at 20:36:23, Thierry Reding wrote:
> > This patch series adds very rudimentary device-tree support for PWM
> > devices. With all of these patches applied (plus one board-specific
> > patch that is not included), I'm able to control the backlight on the
> > device I'm working on using the sysfs interface provided by the pwm-bl
> > driver and the backlight class.
> > 
> > This series is based on Sascha Hauer's series of patches[0] to add a
> > generic PWM framework. The first patch in this series is taken from
> > Sascha's branch, while the second patch enables each PWM chip to provide
> > multiple PWM devices (the Blackfin and PXA drivers have been ported to
> > the framework for reference). Currently a global namespace is still
> > provided to keep backwards-compatibility with the legacy PWM API. In
> > order to achieve this, the number of global PWM devices is limited to
> > 1024. However, patch 2 introduces per-chip indexing of PWM devices in
> > the core and patch 4 adds an API to request a PWM device using a lookup
> > table registered by board setup code. They are supposed to eventually
> > replace pwm_request() and allow to get rid of the global namespace. The
> > device tree support code does not use the global namespace.
> > 
> ..snip..
> ..snip..
> 
> > 
> > The whole series is based on the linux-next tree from 20120405. I think
> > I've addressed all of the concerns raised in the first five versions. I
> > have also pushed this version of the series to the PWM subsystem
> > repository[1].
> > 
> > Thierry
> > 
> > [0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
> > [1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6
> > 
> > Sascha Hauer (4):
> >   pwm: Add PWM framework support
> >   ARM i.MX: Move i.MX pwm driver to pwm framework
> >   ARM Samsung: Move s3c pwm driver to pwm framework
> >   ARM vt8500: Move vt8500 pwm driver to pwm framework
> > 
> > Shawn Guo (1):
> >   pwm: add pwm-mxs support
> > 
> > Simon Que (1):
> >   ARM: tegra: Fix PWM clock programming
> > 
> > Thierry Reding (11):
> >   pwm: Allow chips to support multiple PWMs
> >   pwm: Add debugfs interface
> >   pwm: Add table-based lookup for static mappings
> >   pwm: Add device tree support
> >   ARM: tegra: Provide clock for only one PWM controller
> >   pwm: Add NVIDIA Tegra SoC support
> >   pwm: tegra: Add device tree support
> >   pwm: Move Blackfin PWM driver to PWM framework
> >   pwm: Move PXA PWM driver to PWM framework
> >   pwm-backlight: Add rudimentary device tree support
> >   pwm: Take over maintainership of the PWM subsystem
> > 
> 
> I am planning to add PWM support in Linux for TI AM335x SOC. 
> I am taking your new PWM framework as reference.
> 
> I see a lot of ack's for the patches. I see that you are maintaining these patches in a separate repo.
> However, I couldn't find any of these 
> patches in the mainline kernel (including recent 3.5-rc2). 
> Do you have any plans for pushing this again to mainline kernel. 

Yes, as a matter of fact I'm in the process of getting the series ready for
inclusion into linux-next. I expect them to go into 3.6.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 00/17] Add PWM framework and device tree support
@ 2012-06-14 12:53         ` Thierry Reding
  0 siblings, 0 replies; 78+ messages in thread
From: Thierry Reding @ 2012-06-14 12:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jun 14, 2012 at 12:47:13PM +0000, Hebbar, Gururaja wrote:
> On Tue, Apr 10, 2012 at 20:36:23, Thierry Reding wrote:
> > This patch series adds very rudimentary device-tree support for PWM
> > devices. With all of these patches applied (plus one board-specific
> > patch that is not included), I'm able to control the backlight on the
> > device I'm working on using the sysfs interface provided by the pwm-bl
> > driver and the backlight class.
> > 
> > This series is based on Sascha Hauer's series of patches[0] to add a
> > generic PWM framework. The first patch in this series is taken from
> > Sascha's branch, while the second patch enables each PWM chip to provide
> > multiple PWM devices (the Blackfin and PXA drivers have been ported to
> > the framework for reference). Currently a global namespace is still
> > provided to keep backwards-compatibility with the legacy PWM API. In
> > order to achieve this, the number of global PWM devices is limited to
> > 1024. However, patch 2 introduces per-chip indexing of PWM devices in
> > the core and patch 4 adds an API to request a PWM device using a lookup
> > table registered by board setup code. They are supposed to eventually
> > replace pwm_request() and allow to get rid of the global namespace. The
> > device tree support code does not use the global namespace.
> > 
> ..snip..
> ..snip..
> 
> > 
> > The whole series is based on the linux-next tree from 20120405. I think
> > I've addressed all of the concerns raised in the first five versions. I
> > have also pushed this version of the series to the PWM subsystem
> > repository[1].
> > 
> > Thierry
> > 
> > [0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
> > [1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6
> > 
> > Sascha Hauer (4):
> >   pwm: Add PWM framework support
> >   ARM i.MX: Move i.MX pwm driver to pwm framework
> >   ARM Samsung: Move s3c pwm driver to pwm framework
> >   ARM vt8500: Move vt8500 pwm driver to pwm framework
> > 
> > Shawn Guo (1):
> >   pwm: add pwm-mxs support
> > 
> > Simon Que (1):
> >   ARM: tegra: Fix PWM clock programming
> > 
> > Thierry Reding (11):
> >   pwm: Allow chips to support multiple PWMs
> >   pwm: Add debugfs interface
> >   pwm: Add table-based lookup for static mappings
> >   pwm: Add device tree support
> >   ARM: tegra: Provide clock for only one PWM controller
> >   pwm: Add NVIDIA Tegra SoC support
> >   pwm: tegra: Add device tree support
> >   pwm: Move Blackfin PWM driver to PWM framework
> >   pwm: Move PXA PWM driver to PWM framework
> >   pwm-backlight: Add rudimentary device tree support
> >   pwm: Take over maintainership of the PWM subsystem
> > 
> 
> I am planning to add PWM support in Linux for TI AM335x SOC. 
> I am taking your new PWM framework as reference.
> 
> I see a lot of ack's for the patches. I see that you are maintaining these patches in a separate repo.
> However, I couldn't find any of these 
> patches in the mainline kernel (including recent 3.5-rc2). 
> Do you have any plans for pushing this again to mainline kernel. 

Yes, as a matter of fact I'm in the process of getting the series ready for
inclusion into linux-next. I expect them to go into 3.6.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120614/14e9da76/attachment-0001.sig>

^ permalink raw reply	[flat|nested] 78+ messages in thread

* RE: [PATCH v6 00/17] Add PWM framework and device tree support
  2012-06-14 12:53         ` Thierry Reding
@ 2012-06-14 13:36           ` Hebbar, Gururaja
  -1 siblings, 0 replies; 78+ messages in thread
From: Hebbar, Gururaja @ 2012-06-14 13:36 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Grant Likely, Matthias Kaehlcke, Lars-Peter Clausen,
	Sascha Hauer, Mike Frysinger, Arnd Bergmann, Stephen Warren,
	devicetree-discuss, Rob Herring, Mitch Bradley, Bernhard Walle,
	linux-tegra, linux-arm-kernel, Eric Miao, Ryan Mallon,
	Mark Brown, Richard Purdie, Colin Cross, Olof Johansson,
	Shawn Guo, Kurt Van Dijck

On Thu, Jun 14, 2012 at 18:23:29, Thierry Reding wrote:
> On Thu, Jun 14, 2012 at 12:47:13PM +0000, Hebbar, Gururaja wrote:
> > On Tue, Apr 10, 2012 at 20:36:23, Thierry Reding wrote:
> > > This patch series adds very rudimentary device-tree support for PWM
> > > devices. With all of these patches applied (plus one board-specific
> > > patch that is not included), I'm able to control the backlight on the
> > > device I'm working on using the sysfs interface provided by the pwm-bl
> > > driver and the backlight class.
> > > 
> > > This series is based on Sascha Hauer's series of patches[0] to add a
> > > generic PWM framework. The first patch in this series is taken from
> > > Sascha's branch, while the second patch enables each PWM chip to provide
> > > multiple PWM devices (the Blackfin and PXA drivers have been ported to
> > > the framework for reference). Currently a global namespace is still
> > > provided to keep backwards-compatibility with the legacy PWM API. In
> > > order to achieve this, the number of global PWM devices is limited to
> > > 1024. However, patch 2 introduces per-chip indexing of PWM devices in
> > > the core and patch 4 adds an API to request a PWM device using a lookup
> > > table registered by board setup code. They are supposed to eventually
> > > replace pwm_request() and allow to get rid of the global namespace. The
> > > device tree support code does not use the global namespace.
> > > 
> > ..snip..
> > ..snip..
> > 
> > > 
> > > The whole series is based on the linux-next tree from 20120405. I think
> > > I've addressed all of the concerns raised in the first five versions. I
> > > have also pushed this version of the series to the PWM subsystem
> > > repository[1].
> > > 
> > > Thierry
> > > 
> > > [0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
> > > [1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6
> > > 

..snip..
..snip..

> > > 
> > 
> > I am planning to add PWM support in Linux for TI AM335x SOC. 
> > I am taking your new PWM framework as reference.
> > 
> > I see a lot of ack's for the patches. I see that you are maintaining these patches in a separate repo.
> > However, I couldn't find any of these 
> > patches in the mainline kernel (including recent 3.5-rc2). 
> > Do you have any plans for pushing this again to mainline kernel. 
> 
> Yes, as a matter of fact I'm in the process of getting the series ready for
> inclusion into linux-next. I expect them to go into 3.6.
> 
> Thierry
> 

Thanks. Eagerly looking forward to it.

Thanks & Regards, 
Gururaja

^ permalink raw reply	[flat|nested] 78+ messages in thread

* [PATCH v6 00/17] Add PWM framework and device tree support
@ 2012-06-14 13:36           ` Hebbar, Gururaja
  0 siblings, 0 replies; 78+ messages in thread
From: Hebbar, Gururaja @ 2012-06-14 13:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jun 14, 2012 at 18:23:29, Thierry Reding wrote:
> On Thu, Jun 14, 2012 at 12:47:13PM +0000, Hebbar, Gururaja wrote:
> > On Tue, Apr 10, 2012 at 20:36:23, Thierry Reding wrote:
> > > This patch series adds very rudimentary device-tree support for PWM
> > > devices. With all of these patches applied (plus one board-specific
> > > patch that is not included), I'm able to control the backlight on the
> > > device I'm working on using the sysfs interface provided by the pwm-bl
> > > driver and the backlight class.
> > > 
> > > This series is based on Sascha Hauer's series of patches[0] to add a
> > > generic PWM framework. The first patch in this series is taken from
> > > Sascha's branch, while the second patch enables each PWM chip to provide
> > > multiple PWM devices (the Blackfin and PXA drivers have been ported to
> > > the framework for reference). Currently a global namespace is still
> > > provided to keep backwards-compatibility with the legacy PWM API. In
> > > order to achieve this, the number of global PWM devices is limited to
> > > 1024. However, patch 2 introduces per-chip indexing of PWM devices in
> > > the core and patch 4 adds an API to request a PWM device using a lookup
> > > table registered by board setup code. They are supposed to eventually
> > > replace pwm_request() and allow to get rid of the global namespace. The
> > > device tree support code does not use the global namespace.
> > > 
> > ..snip..
> > ..snip..
> > 
> > > 
> > > The whole series is based on the linux-next tree from 20120405. I think
> > > I've addressed all of the concerns raised in the first five versions. I
> > > have also pushed this version of the series to the PWM subsystem
> > > repository[1].
> > > 
> > > Thierry
> > > 
> > > [0]: http://git.pengutronix.de/?p=imx/linux-2.6.git;a=shortlog;h=refs/heads/pwmlib
> > > [1]: http://gitorious.org/linux-pwm/linux-pwm/trees/pwm-v6
> > > 

..snip..
..snip..

> > > 
> > 
> > I am planning to add PWM support in Linux for TI AM335x SOC. 
> > I am taking your new PWM framework as reference.
> > 
> > I see a lot of ack's for the patches. I see that you are maintaining these patches in a separate repo.
> > However, I couldn't find any of these 
> > patches in the mainline kernel (including recent 3.5-rc2). 
> > Do you have any plans for pushing this again to mainline kernel. 
> 
> Yes, as a matter of fact I'm in the process of getting the series ready for
> inclusion into linux-next. I expect them to go into 3.6.
> 
> Thierry
> 

Thanks. Eagerly looking forward to it.

Thanks & Regards, 
Gururaja

^ permalink raw reply	[flat|nested] 78+ messages in thread

end of thread, other threads:[~2012-06-14 13:36 UTC | newest]

Thread overview: 78+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-10 15:06 [PATCH v6 00/17] Add PWM framework and device tree support Thierry Reding
2012-04-10 15:06 ` Thierry Reding
     [not found] ` <1334070400-25013-1-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-04-10 15:06   ` [PATCH v6 01/17] pwm: Add PWM framework support Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 02/17] pwm: Allow chips to support multiple PWMs Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 03/17] pwm: Add debugfs interface Thierry Reding
2012-04-10 15:06     ` Thierry Reding
     [not found]     ` <1334070400-25013-4-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-04-11 12:33       ` Shawn Guo
2012-04-11 12:33         ` Shawn Guo
     [not found]         ` <20120411123352.GA2445-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-04-11 12:38           ` Thierry Reding
2012-04-11 12:38             ` Thierry Reding
2012-04-11 15:11           ` Mark Brown
2012-04-11 15:11             ` Mark Brown
2012-04-10 15:06   ` [PATCH v6 04/17] pwm: Add table-based lookup for static mappings Thierry Reding
2012-04-10 15:06     ` Thierry Reding
     [not found]     ` <1334070400-25013-5-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-04-11 12:38       ` Shawn Guo
2012-04-11 12:38         ` Shawn Guo
     [not found]         ` <20120411123815.GB2445-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-04-11 12:51           ` Thierry Reding
2012-04-11 12:51             ` Thierry Reding
2012-04-13  9:47       ` Mark Brown
2012-04-13  9:47         ` Mark Brown
2012-04-10 15:06   ` [PATCH v6 05/17] pwm: Add device tree support Thierry Reding
2012-04-10 15:06     ` Thierry Reding
     [not found]     ` <1334070400-25013-6-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-04-11 13:02       ` Shawn Guo
2012-04-11 13:02         ` Shawn Guo
     [not found]         ` <20120411130208.GC2445-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-04-11 13:14           ` Thierry Reding
2012-04-11 13:14             ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 06/17] ARM: tegra: Fix PWM clock programming Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 07/17] ARM: tegra: Provide clock for only one PWM controller Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 08/17] pwm: Add NVIDIA Tegra SoC support Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 09/17] pwm: tegra: Add device tree support Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 10/17] pwm: Move Blackfin PWM driver to PWM framework Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 11/17] pwm: Move PXA " Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 12/17] ARM i.MX: Move i.MX pwm driver to pwm framework Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 13/17] ARM Samsung: Move s3c " Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 14/17] ARM vt8500: Move vt8500 " Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 15/17] pwm: add pwm-mxs support Thierry Reding
2012-04-10 15:06     ` Thierry Reding
2012-04-10 15:06   ` [PATCH v6 16/17] pwm-backlight: Add rudimentary device tree support Thierry Reding
2012-04-10 15:06     ` Thierry Reding
     [not found]     ` <1334070400-25013-17-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-04-11 13:28       ` Shawn Guo
2012-04-11 13:28         ` Shawn Guo
2012-04-13  9:48       ` Mark Brown
2012-04-13  9:48         ` Mark Brown
2012-04-10 15:06   ` [PATCH v6 17/17] pwm: Take over maintainership of the PWM subsystem Thierry Reding
2012-04-10 15:06     ` Thierry Reding
     [not found]     ` <1334070400-25013-18-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-04-10 19:51       ` Arnd Bergmann
2012-04-10 19:51         ` Arnd Bergmann
     [not found]         ` <201204101951.44198.arnd-r2nGTMty4D4@public.gmane.org>
2012-04-12  7:22           ` Thierry Reding
2012-04-12  7:22             ` Thierry Reding
     [not found]             ` <20120412072206.GB18252-RM9K5IK7kjIQXX3q8xo1gnVAuStQJXxyR5q1nwbD4aMs9pC9oP6+/A@public.gmane.org>
2012-04-12 11:34               ` Arnd Bergmann
2012-04-12 11:34                 ` Arnd Bergmann
     [not found]                 ` <201204121134.24892.arnd-r2nGTMty4D4@public.gmane.org>
2012-04-12 12:27                   ` Thierry Reding
2012-04-12 12:27                     ` Thierry Reding
     [not found]                     ` <20120412122740.GB5353-RM9K5IK7kjIyiCvfTdI0JKcOhU4Rzj621B7CTYaBSLdn68oJJulU0Q@public.gmane.org>
2012-04-16  5:50                       ` Thierry Reding
2012-04-16  5:50                         ` Thierry Reding
     [not found]                         ` <20120416055012.GA3037-RM9K5IK7kjIyiCvfTdI0JKcOhU4Rzj621B7CTYaBSLdn68oJJulU0Q@public.gmane.org>
2012-04-16 15:02                           ` Arnd Bergmann
2012-04-16 15:02                             ` Arnd Bergmann
2012-04-10 20:58       ` Sascha Hauer
2012-04-10 20:58         ` Sascha Hauer
2012-06-14 12:47   ` [PATCH v6 00/17] Add PWM framework and device tree support Hebbar, Gururaja
2012-06-14 12:47     ` Hebbar, Gururaja
     [not found]     ` <1BAFE6F6C881BF42822005164F1491C33EA21A9F-Er742YJ7I/eIQmiDNMet8wC/G2K4zDHf@public.gmane.org>
2012-06-14 12:53       ` Thierry Reding
2012-06-14 12:53         ` Thierry Reding
2012-06-14 13:36         ` Hebbar, Gururaja
2012-06-14 13:36           ` Hebbar, Gururaja
2012-04-29 15:40 ` Eric Bénard
2012-04-29 15:40   ` Eric Bénard

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.