All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/16] Add PWM framework and device tree support
@ 2012-03-28 14:33 ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 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 16 makes me the new maintainer of the PWM subsystem.

The whole series is based on the linux-next tree from 20120328. I think
I've addressed all of the concerns raised in the first four versions.

Thierry

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

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

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

 .../devicetree/bindings/pwm/nvidia,tegra20-pwm.txt |   18 +
 Documentation/devicetree/bindings/pwm/pwm.txt      |   53 ++
 .../bindings/video/backlight/pwm-backlight         |   30 +
 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 -
 arch/blackfin/kernel/pwm.c                         |  100 ---
 drivers/Kconfig                                    |    2 +
 drivers/Makefile                                   |    1 +
 drivers/pwm/Kconfig                                |   67 ++
 drivers/pwm/Makefile                               |    7 +
 drivers/pwm/core.c                                 |  679 ++++++++++++++++++++
 drivers/pwm/pwm-bfin.c                             |  164 +++++
 arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c   |  182 ++----
 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                   |  145 ++++-
 include/linux/of_pwm.h                             |   34 +
 include/linux/pwm.h                                |  115 ++++
 include/linux/pwm_backlight.h                      |    1 +
 35 files changed, 1967 insertions(+), 638 deletions(-)
 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
 delete mode 100644 arch/blackfin/kernel/pwm.c
 create mode 100644 drivers/pwm/Kconfig
 create mode 100644 drivers/pwm/Makefile
 create mode 100644 drivers/pwm/core.c
 create mode 100644 drivers/pwm/pwm-bfin.c
 rename arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c (58%)
 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%)
 create mode 100644 include/linux/of_pwm.h

-- 
1.7.9.4

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

* [PATCH v5 00/16] Add PWM framework and device tree support
@ 2012-03-28 14:33 ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 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 16 makes me the new maintainer of the PWM subsystem.

The whole series is based on the linux-next tree from 20120328. I think
I've addressed all of the concerns raised in the first four versions.

Thierry

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

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

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

 .../devicetree/bindings/pwm/nvidia,tegra20-pwm.txt |   18 +
 Documentation/devicetree/bindings/pwm/pwm.txt      |   53 ++
 .../bindings/video/backlight/pwm-backlight         |   30 +
 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 -
 arch/blackfin/kernel/pwm.c                         |  100 ---
 drivers/Kconfig                                    |    2 +
 drivers/Makefile                                   |    1 +
 drivers/pwm/Kconfig                                |   67 ++
 drivers/pwm/Makefile                               |    7 +
 drivers/pwm/core.c                                 |  679 ++++++++++++++++++++
 drivers/pwm/pwm-bfin.c                             |  164 +++++
 arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c   |  182 ++----
 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                   |  145 ++++-
 include/linux/of_pwm.h                             |   34 +
 include/linux/pwm.h                                |  115 ++++
 include/linux/pwm_backlight.h                      |    1 +
 35 files changed, 1967 insertions(+), 638 deletions(-)
 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
 delete mode 100644 arch/blackfin/kernel/pwm.c
 create mode 100644 drivers/pwm/Kconfig
 create mode 100644 drivers/pwm/Makefile
 create mode 100644 drivers/pwm/core.c
 create mode 100644 drivers/pwm/pwm-bfin.c
 rename arch/arm/plat-mxc/pwm.c => drivers/pwm/pwm-imx.c (58%)
 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%)
 create mode 100644 include/linux/of_pwm.h

-- 
1.7.9.4

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

* [PATCH v5 01/16] pwm: Add PWM framework support
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
---
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 c934395..d68feef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5429,6 +5429,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..db2f048
--- /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: 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..4bad29c 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
+ * @label: for diagnostics
+ * @owner: helps prevent removal of modules exporting active PWMs
+ * @ops: The callbacks for this PWM
+ */
+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.4

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

* [PATCH v5 01/16] pwm: Add PWM framework support
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
---
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 c934395..d68feef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5429,6 +5429,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..db2f048
--- /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: 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..4bad29c 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
+ * @label: for diagnostics
+ * @owner: helps prevent removal of modules exporting active PWMs
+ * @ops: The callbacks for this PWM
+ */
+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.4

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

* [PATCH v5 02/16] pwm: Allow chips to support multiple PWMs
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
---
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 db2f048..46dcbe1 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: 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 4bad29c..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
- * @label: for diagnostics
- * @owner: helps prevent removal of modules exporting active PWMs
- * @ops: The callbacks for this PWM
+ * 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.4

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

* [PATCH v5 02/16] pwm: Allow chips to support multiple PWMs
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
---
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 db2f048..46dcbe1 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: 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 4bad29c..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
- * @label: for diagnostics
- * @owner: helps prevent removal of modules exporting active PWMs
- * @ops: The callbacks for this PWM
+ * 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.4

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

* [PATCH v5 03/16] pwm: Add debugfs interface
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
---
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 46dcbe1..12a643d 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.4

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

* [PATCH v5 03/16] pwm: Add debugfs interface
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
---
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 46dcbe1..12a643d 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.4

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 v5:
- new patch with table-based lookup for statically registered devices
- update Documentation/pwm.txt

 Documentation/pwm.txt |   27 +++++++--
 drivers/pwm/core.c    |  152 ++++++++++++++++++++++++++++++++++++++++++++-----
 include/linux/pwm.h   |   20 +++++++
 3 files changed, 180 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 12a643d..77961e0 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: 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,113 @@ 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--) {
+		if (table->provider && table->consumer)
+			list_add_tail(&table->list, &pwm_lookup_list);
+		else
+			pr_err("%s(): provider/consumer must be specified\n",
+			       __func__);
+
+		table++;
+	}
+
+	mutex_unlock(&pwm_lookup_lock);
+}
+
+/**
+ * pwm_get() - look up and request a PWM device
+ * @dev: device for PWM consumer
+ * @provider: name of provider PWM chip
+ * @index: per-chip index of PWM to request
+ *
+ * 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 *consumer)
+{
+	const char *device_name = dev ? dev_name(dev): NULL;
+	struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
+	struct pwm_chip *chip = NULL;
+	struct pwm_lookup *p;
+	unsigned int index;
+
+	/*
+	 * 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.
+	 */
+	mutex_lock(&pwm_lookup_lock);
+
+	if (device_name)
+		list_for_each_entry(p, &pwm_lookup_list, list)
+			if (strcmp(p->consumer, device_name) == 0) {
+				chip = pwmchip_find_by_name(p->provider);
+				consumer = device_name;
+				index = p->index;
+				break;
+			}
+
+	if (!chip && consumer)
+		list_for_each_entry(p, &pwm_lookup_list, list)
+			if (strcmp(p->consumer, consumer) == 0) {
+				chip = pwmchip_find_by_name(p->provider);
+				index = p->index;
+				break;
+			}
+
+	if (chip)
+		pwm = pwm_request_from_chip(chip, index, consumer);
+
+	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..a32d2b7 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -115,6 +115,26 @@ 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 *consumer;
+};
+
+#define PWM_LOOKUP(_provider, _index, _consumer)	\
+	{						\
+		.provider = _provider,			\
+		.index = _index,			\
+		.consumer = _consumer,			\
+	}
+
+void pwm_add_table(struct pwm_lookup *table, size_t num);
+
 #endif
 
 #endif /* __LINUX_PWM_H */
-- 
1.7.9.4

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 v5:
- new patch with table-based lookup for statically registered devices
- update Documentation/pwm.txt

 Documentation/pwm.txt |   27 +++++++--
 drivers/pwm/core.c    |  152 ++++++++++++++++++++++++++++++++++++++++++++-----
 include/linux/pwm.h   |   20 +++++++
 3 files changed, 180 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 12a643d..77961e0 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: 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,113 @@ 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--) {
+		if (table->provider && table->consumer)
+			list_add_tail(&table->list, &pwm_lookup_list);
+		else
+			pr_err("%s(): provider/consumer must be specified\n",
+			       __func__);
+
+		table++;
+	}
+
+	mutex_unlock(&pwm_lookup_lock);
+}
+
+/**
+ * pwm_get() - look up and request a PWM device
+ * @dev: device for PWM consumer
+ * @provider: name of provider PWM chip
+ * @index: per-chip index of PWM to request
+ *
+ * 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 *consumer)
+{
+	const char *device_name = dev ? dev_name(dev): NULL;
+	struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
+	struct pwm_chip *chip = NULL;
+	struct pwm_lookup *p;
+	unsigned int index;
+
+	/*
+	 * 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.
+	 */
+	mutex_lock(&pwm_lookup_lock);
+
+	if (device_name)
+		list_for_each_entry(p, &pwm_lookup_list, list)
+			if (strcmp(p->consumer, device_name) == 0) {
+				chip = pwmchip_find_by_name(p->provider);
+				consumer = device_name;
+				index = p->index;
+				break;
+			}
+
+	if (!chip && consumer)
+		list_for_each_entry(p, &pwm_lookup_list, list)
+			if (strcmp(p->consumer, consumer) == 0) {
+				chip = pwmchip_find_by_name(p->provider);
+				index = p->index;
+				break;
+			}
+
+	if (chip)
+		pwm = pwm_request_from_chip(chip, index, consumer);
+
+	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..a32d2b7 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -115,6 +115,26 @@ 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 *consumer;
+};
+
+#define PWM_LOOKUP(_provider, _index, _consumer)	\
+	{						\
+		.provider = _provider,			\
+		.index = _index,			\
+		.consumer = _consumer,			\
+	}
+
+void pwm_add_table(struct pwm_lookup *table, size_t num);
+
 #endif
 
 #endif /* __LINUX_PWM_H */
-- 
1.7.9.4

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

* [PATCH v5 05/16] pwm: Add device tree support
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
---
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 |   53 ++++++++++
 drivers/pwm/core.c                            |  135 ++++++++++++++++++++++++-
 include/linux/of_pwm.h                        |   34 +++++++
 include/linux/pwm.h                           |    6 ++
 4 files changed, 226 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm.txt
 create mode 100644 include/linux/of_pwm.h

diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
new file mode 100644
index 0000000..bf2addc
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm.txt
@@ -0,0 +1,53 @@
+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.
+
+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 77961e0..c22dd9a 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -20,6 +20,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/of_pwm.h>
 #include <linux/pwm.h>
 #include <linux/radix-tree.h>
 #include <linux/list.h>
@@ -129,6 +130,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 ERR_PTR(-ENODEV);
+
+	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 +237,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 +270,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 +399,85 @@ 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
+ * @index: index of the PWM
+ *
+ * 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.
+ */
+struct pwm_device *of_pwm_request(struct device_node *np, int index)
+{
+	struct pwm_device *pwm = NULL;
+	struct of_phandle_args args;
+	struct pwm_chip *pc;
+	int err;
+
+	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;
+
+	err = of_property_read_string_index(np, "pwm-names", index,
+					    &pwm->label);
+	if (err < 0) {
+		/*
+		 * Fallback to name of user node if the name cannot be parsed
+		 * from the "pwm-names" property (e.g. the property doesn't
+		 * exist or an error occurred).
+		 */
+		pwm->label = np->name;
+	}
+
+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
@@ -380,8 +506,9 @@ void __init pwm_add_table(struct pwm_lookup *table, size_t num)
  * @provider: name of provider PWM chip
  * @index: per-chip index of PWM to request
  *
- * 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.
@@ -394,6 +521,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *consumer)
 	struct pwm_lookup *p;
 	unsigned int index;
 
+	/* look up via DT first */
+	if (dev && dev->of_node)
+		return of_pwm_request(dev->of_node, 0);
+
 	/*
 	 * 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/of_pwm.h b/include/linux/of_pwm.h
new file mode 100644
index 0000000..8c4ed9f
--- /dev/null
+++ b/include/linux/of_pwm.h
@@ -0,0 +1,34 @@
+/*
+ * OF helpers for the PWM API
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __LINUX_OF_PWM_H
+#define __LINUX_OF_PWM_H
+
+#include <linux/err.h>
+#include <linux/pwm.h>
+
+struct device_node;
+
+#ifdef CONFIG_OF
+
+struct pwm_device *of_pwm_request(struct device_node *np, int index);
+
+#else
+
+static inline struct pwm_device *of_pwm_request(struct device_node *np,
+						int index)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
+#endif /* CONFIG_OF */
+
+#endif /* __LINUX_OF_PWM_H */
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index a32d2b7..e19c92a 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.4

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

* [PATCH v5 05/16] pwm: Add device tree support
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
---
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 |   53 ++++++++++
 drivers/pwm/core.c                            |  135 ++++++++++++++++++++++++-
 include/linux/of_pwm.h                        |   34 +++++++
 include/linux/pwm.h                           |    6 ++
 4 files changed, 226 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm.txt
 create mode 100644 include/linux/of_pwm.h

diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
new file mode 100644
index 0000000..bf2addc
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm.txt
@@ -0,0 +1,53 @@
+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.
+
+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 77961e0..c22dd9a 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -20,6 +20,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/of_pwm.h>
 #include <linux/pwm.h>
 #include <linux/radix-tree.h>
 #include <linux/list.h>
@@ -129,6 +130,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 ERR_PTR(-ENODEV);
+
+	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 +237,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 +270,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 +399,85 @@ 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
+ * @index: index of the PWM
+ *
+ * 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.
+ */
+struct pwm_device *of_pwm_request(struct device_node *np, int index)
+{
+	struct pwm_device *pwm = NULL;
+	struct of_phandle_args args;
+	struct pwm_chip *pc;
+	int err;
+
+	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;
+
+	err = of_property_read_string_index(np, "pwm-names", index,
+					    &pwm->label);
+	if (err < 0) {
+		/*
+		 * Fallback to name of user node if the name cannot be parsed
+		 * from the "pwm-names" property (e.g. the property doesn't
+		 * exist or an error occurred).
+		 */
+		pwm->label = np->name;
+	}
+
+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
@@ -380,8 +506,9 @@ void __init pwm_add_table(struct pwm_lookup *table, size_t num)
  * @provider: name of provider PWM chip
  * @index: per-chip index of PWM to request
  *
- * 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.
@@ -394,6 +521,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *consumer)
 	struct pwm_lookup *p;
 	unsigned int index;
 
+	/* look up via DT first */
+	if (dev && dev->of_node)
+		return of_pwm_request(dev->of_node, 0);
+
 	/*
 	 * 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/of_pwm.h b/include/linux/of_pwm.h
new file mode 100644
index 0000000..8c4ed9f
--- /dev/null
+++ b/include/linux/of_pwm.h
@@ -0,0 +1,34 @@
+/*
+ * OF helpers for the PWM API
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __LINUX_OF_PWM_H
+#define __LINUX_OF_PWM_H
+
+#include <linux/err.h>
+#include <linux/pwm.h>
+
+struct device_node;
+
+#ifdef CONFIG_OF
+
+struct pwm_device *of_pwm_request(struct device_node *np, int index);
+
+#else
+
+static inline struct pwm_device *of_pwm_request(struct device_node *np,
+						int index)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
+#endif /* CONFIG_OF */
+
+#endif /* __LINUX_OF_PWM_H */
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index a32d2b7..e19c92a 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.4

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

* [PATCH v5 06/16] ARM: tegra: Fix PWM clock programming
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 06/16] ARM: tegra: Fix PWM clock programming
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 07/16] ARM: tegra: Provide clock for only one PWM controller
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 07/16] ARM: tegra: Provide clock for only one PWM controller
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 08/16] pwm: Add NVIDIA Tegra SoC support
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
---
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..2219be1
--- /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 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 -ENODEV;
+	}
+
+	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.4

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

* [PATCH v5 08/16] pwm: Add NVIDIA Tegra SoC support
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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>
---
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..2219be1
--- /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 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 -ENODEV;
+	}
+
+	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.4

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 e20b419..2180954 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_device.dev.platform_data),
 	OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB3_BASE, "tegra-ehci.2",
 		       &tegra_ehci3_device.dev.platform_data),
+	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 2219be1..981f28a 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.4

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 e20b419..2180954 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_device.dev.platform_data),
 	OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB3_BASE, "tegra-ehci.2",
 		       &tegra_ehci3_device.dev.platform_data),
+	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 2219be1..981f28a 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.4

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

* [PATCH v5 10/16] pwm: Move Blackfin PWM driver to PWM framework
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 -
 arch/blackfin/kernel/pwm.c    |  100 -------------------------
 drivers/pwm/Kconfig           |    9 +++
 drivers/pwm/Makefile          |    1 +
 drivers/pwm/pwm-bfin.c        |  164 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 174 insertions(+), 111 deletions(-)
 delete mode 100644 arch/blackfin/kernel/pwm.c
 create mode 100644 drivers/pwm/pwm-bfin.c

diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index c1269a1..e08f39b 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/arch/blackfin/kernel/pwm.c b/arch/blackfin/kernel/pwm.c
deleted file mode 100644
index 33f5942..0000000
--- a/arch/blackfin/kernel/pwm.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Blackfin Pulse Width Modulation (PWM) core
- *
- * Copyright (c) 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/pwm.h>
-#include <linux/slab.h>
-
-#include <asm/gptimers.h>
-#include <asm/portmux.h>
-
-struct pwm_device {
-	unsigned id;
-	unsigned short pin;
-};
-
-static const unsigned short pwm_to_gptimer_per[] = {
-	P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
-	P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
-};
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int ret;
-
-	/* XXX: pwm_id really should be unsigned */
-	if (pwm_id < 0)
-		return NULL;
-
-	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
-	if (!pwm)
-		return pwm;
-
-	pwm->id = pwm_id;
-	if (pwm->id >= ARRAY_SIZE(pwm_to_gptimer_per))
-		goto err;
-
-	pwm->pin = pwm_to_gptimer_per[pwm->id];
-	ret = peripheral_request(pwm->pin, label);
-	if (ret)
-		goto err;
-
-	return pwm;
- err:
-	kfree(pwm);
-	return NULL;
-}
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-	peripheral_free(pwm->pin);
-	kfree(pwm);
-}
-EXPORT_SYMBOL(pwm_free);
-
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-{
-	unsigned long period, duty;
-	unsigned long long val;
-
-	if (duty_ns < 0 || duty_ns > period_ns)
-		return -EINVAL;
-
-	val = (unsigned long long)get_sclk() * period_ns;
-	do_div(val, NSEC_PER_SEC);
-	period = val;
-
-	val = (unsigned long long)period * duty_ns;
-	do_div(val, period_ns);
-	duty = period - val;
-
-	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);
-
-	return 0;
-}
-EXPORT_SYMBOL(pwm_config);
-
-int pwm_enable(struct pwm_device *pwm)
-{
-	enable_gptimer(pwm->id);
-	return 0;
-}
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-	disable_gptimer(pwm->id);
-}
-EXPORT_SYMBOL(pwm_disable);
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/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c
new file mode 100644
index 0000000..a0c6bf9
--- /dev/null
+++ b/drivers/pwm/pwm-bfin.c
@@ -0,0 +1,164 @@
+/*
+ * Blackfin Pulse Width Modulation (PWM) core
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#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 bfin_pwm_chip {
+	struct pwm_chip chip;
+};
+
+struct bfin_pwm {
+	unsigned short pin;
+};
+
+static const unsigned short pwm_to_gptimer_per[] = {
+	P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
+	P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
+};
+
+static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct bfin_pwm *priv;
+	int ret;
+
+	if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per))
+		return -EINVAL;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pin = pwm_to_gptimer_per[pwm->hwpwm];
+
+	ret = peripheral_request(priv->pin, NULL);
+	if (ret) {
+		kfree(priv);
+		return ret;
+	}
+
+	pwm_set_chip_data(pwm, priv);
+
+	return 0;
+}
+
+static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	if (priv) {
+		peripheral_free(priv->pin);
+		kfree(priv);
+	}
+}
+
+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;
+
+	if (duty_ns < 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	val = (unsigned long long)get_sclk() * period_ns;
+	do_div(val, NSEC_PER_SEC);
+	period = val;
+
+	val = (unsigned long long)period * duty_ns;
+	do_div(val, period_ns);
+	duty = period - val;
+
+	if (duty >= period)
+		duty = period - 1;
+
+	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;
+}
+
+static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	enable_gptimer(priv->pin);
+
+	return 0;
+}
+
+static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	disable_gptimer(priv->pin);
+}
+
+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.4

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

* [PATCH v5 10/16] pwm: Move Blackfin PWM driver to PWM framework
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 -
 arch/blackfin/kernel/pwm.c    |  100 -------------------------
 drivers/pwm/Kconfig           |    9 +++
 drivers/pwm/Makefile          |    1 +
 drivers/pwm/pwm-bfin.c        |  164 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 174 insertions(+), 111 deletions(-)
 delete mode 100644 arch/blackfin/kernel/pwm.c
 create mode 100644 drivers/pwm/pwm-bfin.c

diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index c1269a1..e08f39b 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/arch/blackfin/kernel/pwm.c b/arch/blackfin/kernel/pwm.c
deleted file mode 100644
index 33f5942..0000000
--- a/arch/blackfin/kernel/pwm.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Blackfin Pulse Width Modulation (PWM) core
- *
- * Copyright (c) 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/pwm.h>
-#include <linux/slab.h>
-
-#include <asm/gptimers.h>
-#include <asm/portmux.h>
-
-struct pwm_device {
-	unsigned id;
-	unsigned short pin;
-};
-
-static const unsigned short pwm_to_gptimer_per[] = {
-	P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
-	P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
-};
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int ret;
-
-	/* XXX: pwm_id really should be unsigned */
-	if (pwm_id < 0)
-		return NULL;
-
-	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
-	if (!pwm)
-		return pwm;
-
-	pwm->id = pwm_id;
-	if (pwm->id >= ARRAY_SIZE(pwm_to_gptimer_per))
-		goto err;
-
-	pwm->pin = pwm_to_gptimer_per[pwm->id];
-	ret = peripheral_request(pwm->pin, label);
-	if (ret)
-		goto err;
-
-	return pwm;
- err:
-	kfree(pwm);
-	return NULL;
-}
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-	peripheral_free(pwm->pin);
-	kfree(pwm);
-}
-EXPORT_SYMBOL(pwm_free);
-
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-{
-	unsigned long period, duty;
-	unsigned long long val;
-
-	if (duty_ns < 0 || duty_ns > period_ns)
-		return -EINVAL;
-
-	val = (unsigned long long)get_sclk() * period_ns;
-	do_div(val, NSEC_PER_SEC);
-	period = val;
-
-	val = (unsigned long long)period * duty_ns;
-	do_div(val, period_ns);
-	duty = period - val;
-
-	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);
-
-	return 0;
-}
-EXPORT_SYMBOL(pwm_config);
-
-int pwm_enable(struct pwm_device *pwm)
-{
-	enable_gptimer(pwm->id);
-	return 0;
-}
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-	disable_gptimer(pwm->id);
-}
-EXPORT_SYMBOL(pwm_disable);
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/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c
new file mode 100644
index 0000000..a0c6bf9
--- /dev/null
+++ b/drivers/pwm/pwm-bfin.c
@@ -0,0 +1,164 @@
+/*
+ * Blackfin Pulse Width Modulation (PWM) core
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#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 bfin_pwm_chip {
+	struct pwm_chip chip;
+};
+
+struct bfin_pwm {
+	unsigned short pin;
+};
+
+static const unsigned short pwm_to_gptimer_per[] = {
+	P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
+	P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
+};
+
+static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct bfin_pwm *priv;
+	int ret;
+
+	if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per))
+		return -EINVAL;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pin = pwm_to_gptimer_per[pwm->hwpwm];
+
+	ret = peripheral_request(priv->pin, NULL);
+	if (ret) {
+		kfree(priv);
+		return ret;
+	}
+
+	pwm_set_chip_data(pwm, priv);
+
+	return 0;
+}
+
+static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	if (priv) {
+		peripheral_free(priv->pin);
+		kfree(priv);
+	}
+}
+
+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;
+
+	if (duty_ns < 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	val = (unsigned long long)get_sclk() * period_ns;
+	do_div(val, NSEC_PER_SEC);
+	period = val;
+
+	val = (unsigned long long)period * duty_ns;
+	do_div(val, period_ns);
+	duty = period - val;
+
+	if (duty >= period)
+		duty = period - 1;
+
+	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;
+}
+
+static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	enable_gptimer(priv->pin);
+
+	return 0;
+}
+
+static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+	disable_gptimer(priv->pin);
+}
+
+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.4

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

* [PATCH v5 11/16] pwm: Move PXA PWM driver to PWM framework
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 11/16] pwm: Move PXA PWM driver to PWM framework
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 12/16] ARM i.MX: Move i.MX pwm driver to pwm framework
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 12/16] ARM i.MX: Move i.MX pwm driver to pwm framework
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 13/16] ARM Samsung: Move s3c pwm driver to pwm framework
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 13/16] ARM Samsung: Move s3c pwm driver to pwm framework
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 14/16] ARM vt8500: Move vt8500 pwm driver to pwm framework
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 14/16] ARM vt8500: Move vt8500 pwm driver to pwm framework
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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.4

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

* [PATCH v5 15/16] pwm-backlight: Add rudimentary device tree support
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33   ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 UTC (permalink / raw)
  To: devicetree-discuss
  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,
	Eric Miao, Shawn Guo, linux-arm-kernel, Kurt Van Dijck

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 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         |   30 ++++
 drivers/video/backlight/Kconfig                    |    2 +-
 drivers/video/backlight/pwm_bl.c                   |  145 +++++++++++++++++---
 include/linux/pwm_backlight.h                      |    1 +
 4 files changed, 157 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..bc2d08d
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
@@ -0,0 +1,30 @@
+pwm-backlight bindings
+
+Required properties:
+  - compatible: "pwm-backlight"
+  - pwms: OF device-tree PWM specification (see PWM binding[0])
+  - num-brightness-levels: number of distinct brightness levels
+  - 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>;
+
+		num-brightness-levels = <8>;
+		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..b996548 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,94 @@ 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;
+	u32 value;
+	int ret;
+
+	if (!node)
+		return -ENODEV;
+
+	memset(data, 0, sizeof(*data));
+
+	ret = of_property_read_u32(node, "num-brightness-levels", &value);
+	if (ret < 0)
+		return ret;
+
+	data->max_brightness = value;
+
+	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 +193,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, "pwm-backlight");
 	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 +248,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 +257,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 +299,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.4

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

* [PATCH v5 15/16] pwm-backlight: Add rudimentary device tree support
@ 2012-03-28 14:33   ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 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         |   30 ++++
 drivers/video/backlight/Kconfig                    |    2 +-
 drivers/video/backlight/pwm_bl.c                   |  145 +++++++++++++++++---
 include/linux/pwm_backlight.h                      |    1 +
 4 files changed, 157 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..bc2d08d
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
@@ -0,0 +1,30 @@
+pwm-backlight bindings
+
+Required properties:
+  - compatible: "pwm-backlight"
+  - pwms: OF device-tree PWM specification (see PWM binding[0])
+  - num-brightness-levels: number of distinct brightness levels
+  - 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>;
+
+		num-brightness-levels = <8>;
+		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..b996548 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,94 @@ 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;
+	u32 value;
+	int ret;
+
+	if (!node)
+		return -ENODEV;
+
+	memset(data, 0, sizeof(*data));
+
+	ret = of_property_read_u32(node, "num-brightness-levels", &value);
+	if (ret < 0)
+		return ret;
+
+	data->max_brightness = value;
+
+	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 +193,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, "pwm-backlight");
 	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 +248,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 +257,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 +299,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.4

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

* [PATCH v5 16/16] pwm: Take over maintainership of the PWM subsystem
  2012-03-28 14:33 ` Thierry Reding
@ 2012-03-28 14:33     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 UTC (permalink / raw)
  To: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
  Cc: Mark Brown, Ryan Mallon, Sascha Hauer, Colin Cross, Rob Herring,
	Lars-Peter Clausen, Richard Purdie, Bernhard Walle,
	Matthias Kaehlcke, linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck

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 d68feef..769055a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5429,10 +5429,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.4

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

* [PATCH v5 16/16] pwm: Take over maintainership of the PWM subsystem
@ 2012-03-28 14:33     ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-28 14:33 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 d68feef..769055a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5429,10 +5429,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.4

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

* Re: [PATCH v5 05/16] pwm: Add device tree support
  2012-03-28 14:33     ` Thierry Reding
@ 2012-03-28 14:53         ` Arnd Bergmann
  -1 siblings, 0 replies; 96+ messages in thread
From: Arnd Bergmann @ 2012-03-28 14:53 UTC (permalink / raw)
  To: Thierry Reding
  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

On Wednesday 28 March 2012, 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>

I'm very happy with how the binding turned out.

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

* [PATCH v5 05/16] pwm: Add device tree support
@ 2012-03-28 14:53         ` Arnd Bergmann
  0 siblings, 0 replies; 96+ messages in thread
From: Arnd Bergmann @ 2012-03-28 14:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday 28 March 2012, 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>

I'm very happy with how the binding turned out.

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

* Re: [PATCH v5 01/16] pwm: Add PWM framework support
  2012-03-28 14:33     ` Thierry Reding
@ 2012-03-29 21:41         ` Mark Brown
  -1 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 21:41 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, 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,
	Mitch Bradley, Mike Frysinger, Eric Miao, Lars-Peter Clausen,
	Ryan Mallon, Shawn Guo, Bernhard Walle

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

On Wed, Mar 28, 2012 at 04:33:43PM +0200, Thierry Reding wrote:
> From: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> 
> This patch adds framework support for PWM (pulse width modulation) devices.

Looking good, hope we can get this in for 3.5!

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] 96+ messages in thread

* [PATCH v5 01/16] pwm: Add PWM framework support
@ 2012-03-29 21:41         ` Mark Brown
  0 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 21:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 28, 2012 at 04:33:43PM +0200, Thierry Reding wrote:
> From: Sascha Hauer <s.hauer@pengutronix.de>
> 
> This patch adds framework support for PWM (pulse width modulation) devices.

Looking good, hope we can get this in for 3.5!

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/20120329/2b4e4011/attachment.sig>

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

* Re: [PATCH v5 05/16] pwm: Add device tree support
  2012-03-28 14:33     ` Thierry Reding
@ 2012-03-29 21:47         ` Mark Brown
  -1 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 21: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: 932 bytes --]

On Wed, Mar 28, 2012 at 04:33:47PM +0200, Thierry Reding wrote:

> +	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.

> +	pwm = pwm_request_from_chip(pc, args->args[0], NULL);
> +	if (IS_ERR(pwm))
> +		return ERR_PTR(-ENODEV);

It feels wrong to override the error code like this rather than passing
the error we got back to the caller.  Is there any great reason for
doing so?

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

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

* [PATCH v5 05/16] pwm: Add device tree support
@ 2012-03-29 21:47         ` Mark Brown
  0 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 21:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 28, 2012 at 04:33:47PM +0200, Thierry Reding wrote:

> +	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.

> +	pwm = pwm_request_from_chip(pc, args->args[0], NULL);
> +	if (IS_ERR(pwm))
> +		return ERR_PTR(-ENODEV);

It feels wrong to override the error code like this rather than passing
the error we got back to the caller.  Is there any great reason for
doing so?
-------------- 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/20120329/68e27476/attachment.sig>

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

* Re: [PATCH v5 02/16] pwm: Allow chips to support multiple PWMs
  2012-03-28 14:33     ` Thierry Reding
@ 2012-03-29 21:50         ` Mark Brown
  -1 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 21:50 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: 549 bytes --]

On Wed, Mar 28, 2012 at 04:33:44PM +0200, Thierry Reding wrote:
> 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.

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] 96+ messages in thread

* [PATCH v5 02/16] pwm: Allow chips to support multiple PWMs
@ 2012-03-29 21:50         ` Mark Brown
  0 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 21:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 28, 2012 at 04:33:44PM +0200, Thierry Reding wrote:
> 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.

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/20120329/03d467ed/attachment.sig>

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

* Re: [PATCH v5 03/16] pwm: Add debugfs interface
  2012-03-28 14:33     ` Thierry Reding
@ 2012-03-29 21:56         ` Mark Brown
  -1 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 21:56 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: 613 bytes --]

On Wed, Mar 28, 2012 at 04:33:45PM +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.

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

I'm not sure I don't find the style with a file per chip more managable,
but I think that's mostly a thing for GPIOs since you get so many of
them in most systems that the file gets large which isn't such an issue
with pwms, you tend to get much fewer of them.  It's certainly a matter
of taste anyway.

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

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

* [PATCH v5 03/16] pwm: Add debugfs interface
@ 2012-03-29 21:56         ` Mark Brown
  0 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 21:56 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 28, 2012 at 04:33:45PM +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.

Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

I'm not sure I don't find the style with a file per chip more managable,
but I think that's mostly a thing for GPIOs since you get so many of
them in most systems that the file gets large which isn't such an issue
with pwms, you tend to get much fewer of them.  It's certainly a matter
of taste anyway.
-------------- 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/20120329/016d21b4/attachment.sig>

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

* Re: [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
  2012-03-28 14:33     ` Thierry Reding
@ 2012-03-29 22:03         ` Mark Brown
  -1 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 22:03 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: 666 bytes --]

On Wed, Mar 28, 2012 at 04:33:46PM +0200, Thierry Reding wrote:

> +	static struct pwm_lookup board_pwm_lookup[] = {
> +		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),
> +	};

The clock and regulator APIs namespace the consumers by struct device -
might this not be sensible here?  pwm_get() does already take the device
as an argument.  It'd feel safer, and for example there's plenty of
phones out there with two backlit displays...

> + * pwm_get() - look up and request a PWM device
> + * @dev: device for PWM consumer
> + * @provider: name of provider PWM chip
> + * @index: per-chip index of PWM to request

The documentation is out of sync with the signature.

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

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
@ 2012-03-29 22:03         ` Mark Brown
  0 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-29 22:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 28, 2012 at 04:33:46PM +0200, Thierry Reding wrote:

> +	static struct pwm_lookup board_pwm_lookup[] = {
> +		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),
> +	};

The clock and regulator APIs namespace the consumers by struct device -
might this not be sensible here?  pwm_get() does already take the device
as an argument.  It'd feel safer, and for example there's plenty of
phones out there with two backlit displays...

> + * pwm_get() - look up and request a PWM device
> + * @dev: device for PWM consumer
> + * @provider: name of provider PWM chip
> + * @index: per-chip index of PWM to request

The documentation is out of sync with the signature.
-------------- 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/20120329/afdf521d/attachment.sig>

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

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


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

* Mark Brown wrote:
> On Wed, Mar 28, 2012 at 04:33:46PM +0200, Thierry Reding wrote:
> 
> > +	static struct pwm_lookup board_pwm_lookup[] = {
> > +		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),
> > +	};
> 
> The clock and regulator APIs namespace the consumers by struct device -
> might this not be sensible here?  pwm_get() does already take the device
> as an argument.  It'd feel safer, and for example there's plenty of
> phones out there with two backlit displays...

That's actually how this is supposed to work. "pwm-backlight" in the above
case is matched against the name of the struct device that you pass in to
pwm_get(). The only difference, at least as far as I can tell, to the clock
and regulator APIs is that a second name is not listed explicitly in the
lookup table.

So compared with the clock and regulator APIs it doesn't make too much sense
to pass both the struct device and the name to pwm_get() because it will
match the device name against the consumer name in the lookup table first and
only use the passed name if no match was found.

In case you have two backlight devices I would expect the following to work:

	static struct pwm_lookup board_pwm_lookup[] = {
		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight.0"),
		PWM_LOOKUP("tegra-pwm", 1, "pwm-backlight.1"),
	};

> 
> > + * pwm_get() - look up and request a PWM device
> > + * @dev: device for PWM consumer
> > + * @provider: name of provider PWM chip
> > + * @index: per-chip index of PWM to request
> 
> The documentation is out of sync with the signature.

Indeed. Thanks.

Thierry

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

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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
@ 2012-03-30  5:06           ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-30  5:06 UTC (permalink / raw)
  To: linux-arm-kernel

* Mark Brown wrote:
> On Wed, Mar 28, 2012 at 04:33:46PM +0200, Thierry Reding wrote:
> 
> > +	static struct pwm_lookup board_pwm_lookup[] = {
> > +		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight"),
> > +	};
> 
> The clock and regulator APIs namespace the consumers by struct device -
> might this not be sensible here?  pwm_get() does already take the device
> as an argument.  It'd feel safer, and for example there's plenty of
> phones out there with two backlit displays...

That's actually how this is supposed to work. "pwm-backlight" in the above
case is matched against the name of the struct device that you pass in to
pwm_get(). The only difference, at least as far as I can tell, to the clock
and regulator APIs is that a second name is not listed explicitly in the
lookup table.

So compared with the clock and regulator APIs it doesn't make too much sense
to pass both the struct device and the name to pwm_get() because it will
match the device name against the consumer name in the lookup table first and
only use the passed name if no match was found.

In case you have two backlight devices I would expect the following to work:

	static struct pwm_lookup board_pwm_lookup[] = {
		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight.0"),
		PWM_LOOKUP("tegra-pwm", 1, "pwm-backlight.1"),
	};

> 
> > + * pwm_get() - look up and request a PWM device
> > + * @dev: device for PWM consumer
> > + * @provider: name of provider PWM chip
> > + * @index: per-chip index of PWM to request
> 
> The documentation is out of sync with the signature.

Indeed. 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/20120330/edbfb298/attachment.sig>

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

* Re: [PATCH v5 05/16] pwm: Add device tree support
  2012-03-29 21:47         ` Mark Brown
@ 2012-03-30  6:24             ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-30  6:24 UTC (permalink / raw)
  To: Mark Brown
  Cc: Lars-Peter Clausen, Ryan Mallon,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Colin Cross,
	Rob Herring, Bernhard Walle, Richard Purdie, Matthias Kaehlcke,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao, Sascha Hauer,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck


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

* Mark Brown wrote:
> On Wed, Mar 28, 2012 at 04:33:47PM +0200, Thierry Reding wrote:
> > +	pwm = pwm_request_from_chip(pc, args->args[0], NULL);
> > +	if (IS_ERR(pwm))
> > +		return ERR_PTR(-ENODEV);
> 
> It feels wrong to override the error code like this rather than passing
> the error we got back to the caller.  Is there any great reason for
> doing so?

Yes, this certainly looks wrong. I must have forgotten to fix this up when
of_pwm_simple_xlate() was converted to return a struct pwm_device instead of
an int. Previous to that pwm_request_from_chip() would return NULL and
-ENODEV seemed like the proper error code for that situation.

I'll change this to just return pwm.

Thanks,
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] 96+ messages in thread

* [PATCH v5 05/16] pwm: Add device tree support
@ 2012-03-30  6:24             ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-30  6:24 UTC (permalink / raw)
  To: linux-arm-kernel

* Mark Brown wrote:
> On Wed, Mar 28, 2012 at 04:33:47PM +0200, Thierry Reding wrote:
> > +	pwm = pwm_request_from_chip(pc, args->args[0], NULL);
> > +	if (IS_ERR(pwm))
> > +		return ERR_PTR(-ENODEV);
> 
> It feels wrong to override the error code like this rather than passing
> the error we got back to the caller.  Is there any great reason for
> doing so?

Yes, this certainly looks wrong. I must have forgotten to fix this up when
of_pwm_simple_xlate() was converted to return a struct pwm_device instead of
an int. Previous to that pwm_request_from_chip() would return NULL and
-ENODEV seemed like the proper error code for that situation.

I'll change this to just return pwm.

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/20120330/162206ae/attachment.sig>

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

* Re: [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
  2012-03-30  5:06           ` Thierry Reding
@ 2012-03-30 10:18               ` Mark Brown
  -1 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-30 10:18 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: 1565 bytes --]

On Fri, Mar 30, 2012 at 07:06:41AM +0200, Thierry Reding wrote:
> * Mark Brown wrote:

> > The clock and regulator APIs namespace the consumers by struct device -
> > might this not be sensible here?  pwm_get() does already take the device
> > as an argument.  It'd feel safer, and for example there's plenty of
> > phones out there with two backlit displays...

> That's actually how this is supposed to work. "pwm-backlight" in the above
> case is matched against the name of the struct device that you pass in to
> pwm_get(). The only difference, at least as far as I can tell, to the clock
> and regulator APIs is that a second name is not listed explicitly in the
> lookup table.

Both clock and regulator APIs map (source) -> (dev, name).  This only
has a mapping (source) -> (dev).

> So compared with the clock and regulator APIs it doesn't make too much sense
> to pass both the struct device and the name to pwm_get() because it will
> match the device name against the consumer name in the lookup table first and
> only use the passed name if no match was found.

This is different to what the clock and regulator APIs do - they look up
the name in the context of the struct device.  This is because...

> In case you have two backlight devices I would expect the following to work:

> 	static struct pwm_lookup board_pwm_lookup[] = {
> 		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight.0"),
> 		PWM_LOOKUP("tegra-pwm", 1, "pwm-backlight.1"),
> 	};

...if a single device uses more than one PWM then the above scheme won't
work - unless I'm missing something?

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

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
@ 2012-03-30 10:18               ` Mark Brown
  0 siblings, 0 replies; 96+ messages in thread
From: Mark Brown @ 2012-03-30 10:18 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Mar 30, 2012 at 07:06:41AM +0200, Thierry Reding wrote:
> * Mark Brown wrote:

> > The clock and regulator APIs namespace the consumers by struct device -
> > might this not be sensible here?  pwm_get() does already take the device
> > as an argument.  It'd feel safer, and for example there's plenty of
> > phones out there with two backlit displays...

> That's actually how this is supposed to work. "pwm-backlight" in the above
> case is matched against the name of the struct device that you pass in to
> pwm_get(). The only difference, at least as far as I can tell, to the clock
> and regulator APIs is that a second name is not listed explicitly in the
> lookup table.

Both clock and regulator APIs map (source) -> (dev, name).  This only
has a mapping (source) -> (dev).

> So compared with the clock and regulator APIs it doesn't make too much sense
> to pass both the struct device and the name to pwm_get() because it will
> match the device name against the consumer name in the lookup table first and
> only use the passed name if no match was found.

This is different to what the clock and regulator APIs do - they look up
the name in the context of the struct device.  This is because...

> In case you have two backlight devices I would expect the following to work:

> 	static struct pwm_lookup board_pwm_lookup[] = {
> 		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight.0"),
> 		PWM_LOOKUP("tegra-pwm", 1, "pwm-backlight.1"),
> 	};

...if a single device uses more than one PWM then the above scheme won't
work - unless I'm missing something?
-------------- 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/20120330/eceb80cf/attachment.sig>

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

* Re: [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
  2012-03-30 10:18               ` Mark Brown
@ 2012-03-30 10:38                   ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-30 10:38 UTC (permalink / raw)
  To: Mark Brown
  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: 2142 bytes --]

* Mark Brown wrote:
> On Fri, Mar 30, 2012 at 07:06:41AM +0200, Thierry Reding wrote:
> > * Mark Brown wrote:
> 
> > > The clock and regulator APIs namespace the consumers by struct device -
> > > might this not be sensible here?  pwm_get() does already take the device
> > > as an argument.  It'd feel safer, and for example there's plenty of
> > > phones out there with two backlit displays...
> 
> > That's actually how this is supposed to work. "pwm-backlight" in the above
> > case is matched against the name of the struct device that you pass in to
> > pwm_get(). The only difference, at least as far as I can tell, to the clock
> > and regulator APIs is that a second name is not listed explicitly in the
> > lookup table.
> 
> Both clock and regulator APIs map (source) -> (dev, name).  This only
> has a mapping (source) -> (dev).
> 
> > So compared with the clock and regulator APIs it doesn't make too much sense
> > to pass both the struct device and the name to pwm_get() because it will
> > match the device name against the consumer name in the lookup table first and
> > only use the passed name if no match was found.
> 
> This is different to what the clock and regulator APIs do - they look up
> the name in the context of the struct device.  This is because...
> 
> > In case you have two backlight devices I would expect the following to work:
> 
> > 	static struct pwm_lookup board_pwm_lookup[] = {
> > 		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight.0"),
> > 		PWM_LOOKUP("tegra-pwm", 1, "pwm-backlight.1"),
> > 	};
> 
> ...if a single device uses more than one PWM then the above scheme won't
> work - unless I'm missing something?

Right, now it makes more sense. So basically if I have a single device using
two PWM devices, then I'll need to have something like this:

	static struct pwm_lookup board_pwm_lookup[] = {
		PWM_LOOKUP("tegra-pwm", 0, "pwm-foo.0", "bar"),
		PWM_LOOKUP("tegra-pwm", 1, "pwm-foo.0", "baz"),
	};

And then of course I'll need to have a second field by which the PWM device
can be identified. Good, I'll fix that up for the next round.

Thierry

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

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
@ 2012-03-30 10:38                   ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-30 10:38 UTC (permalink / raw)
  To: linux-arm-kernel

* Mark Brown wrote:
> On Fri, Mar 30, 2012 at 07:06:41AM +0200, Thierry Reding wrote:
> > * Mark Brown wrote:
> 
> > > The clock and regulator APIs namespace the consumers by struct device -
> > > might this not be sensible here?  pwm_get() does already take the device
> > > as an argument.  It'd feel safer, and for example there's plenty of
> > > phones out there with two backlit displays...
> 
> > That's actually how this is supposed to work. "pwm-backlight" in the above
> > case is matched against the name of the struct device that you pass in to
> > pwm_get(). The only difference, at least as far as I can tell, to the clock
> > and regulator APIs is that a second name is not listed explicitly in the
> > lookup table.
> 
> Both clock and regulator APIs map (source) -> (dev, name).  This only
> has a mapping (source) -> (dev).
> 
> > So compared with the clock and regulator APIs it doesn't make too much sense
> > to pass both the struct device and the name to pwm_get() because it will
> > match the device name against the consumer name in the lookup table first and
> > only use the passed name if no match was found.
> 
> This is different to what the clock and regulator APIs do - they look up
> the name in the context of the struct device.  This is because...
> 
> > In case you have two backlight devices I would expect the following to work:
> 
> > 	static struct pwm_lookup board_pwm_lookup[] = {
> > 		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight.0"),
> > 		PWM_LOOKUP("tegra-pwm", 1, "pwm-backlight.1"),
> > 	};
> 
> ...if a single device uses more than one PWM then the above scheme won't
> work - unless I'm missing something?

Right, now it makes more sense. So basically if I have a single device using
two PWM devices, then I'll need to have something like this:

	static struct pwm_lookup board_pwm_lookup[] = {
		PWM_LOOKUP("tegra-pwm", 0, "pwm-foo.0", "bar"),
		PWM_LOOKUP("tegra-pwm", 1, "pwm-foo.0", "baz"),
	};

And then of course I'll need to have a second field by which the PWM device
can be identified. Good, I'll fix that up for the next round.

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/20120330/c9e19b10/attachment.sig>

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

* Re: [PATCH v5 08/16] pwm: Add NVIDIA Tegra SoC support
  2012-03-28 14:33     ` Thierry Reding
@ 2012-03-30 18:57         ` Stephen Warren
  -1 siblings, 0 replies; 96+ messages in thread
From: Stephen Warren @ 2012-03-30 18:57 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, Richard Purdie, Mark Brown,
	Mitch Bradley, Mike Frysinger, Eric Miao, Lars-Peter Clausen,
	Ryan Mallon, Shawn Guo, Bernhard Walle

On 03/28/2012 08:33 AM, Thierry Reding wrote:
> 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>

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

* [PATCH v5 08/16] pwm: Add NVIDIA Tegra SoC support
@ 2012-03-30 18:57         ` Stephen Warren
  0 siblings, 0 replies; 96+ messages in thread
From: Stephen Warren @ 2012-03-30 18:57 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/28/2012 08:33 AM, Thierry Reding wrote:
> 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>

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

* Re: [PATCH v5 09/16] pwm: tegra: Add device tree support
  2012-03-28 14:33     ` Thierry Reding
@ 2012-03-30 19:00         ` Stephen Warren
  -1 siblings, 0 replies; 96+ messages in thread
From: Stephen Warren @ 2012-03-30 19:00 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, Richard Purdie, Mark Brown,
	Mitch Bradley, Mike Frysinger, Eric Miao, Lars-Peter Clausen,
	Ryan Mallon, Shawn Guo, Bernhard Walle

On 03/28/2012 08:33 AM, Thierry Reding wrote:
> 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>

> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
...
> +#ifdef CONFIG_OF
> +static struct of_device_id tegra_pwm_of_match[] = {
> +	{ .compatible = "nvidia,tegra20-pwm" },
> +	{ .compatible = "nvidia,tegra30-pwm" },

Could you swap those two lines, so that tegra30-pwm matches first. It
makes no difference at present, but might in the future if the driver
actually has to differentiate the two SoCs.

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
@ 2012-03-30 19:00         ` Stephen Warren
  0 siblings, 0 replies; 96+ messages in thread
From: Stephen Warren @ 2012-03-30 19:00 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/28/2012 08:33 AM, Thierry Reding wrote:
> 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>

> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
...
> +#ifdef CONFIG_OF
> +static struct of_device_id tegra_pwm_of_match[] = {
> +	{ .compatible = "nvidia,tegra20-pwm" },
> +	{ .compatible = "nvidia,tegra30-pwm" },

Could you swap those two lines, so that tegra30-pwm matches first. It
makes no difference at present, but might in the future if the driver
actually has to differentiate the two SoCs.

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

* Re: [PATCH v5 15/16] pwm-backlight: Add rudimentary device tree support
  2012-03-28 14:33   ` Thierry Reding
@ 2012-03-30 19:04       ` Stephen Warren
  -1 siblings, 0 replies; 96+ messages in thread
From: Stephen Warren @ 2012-03-30 19:04 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, Richard Purdie, Mark Brown,
	Mitch Bradley, Mike Frysinger, Eric Miao, Lars-Peter Clausen,
	Ryan Mallon, Shawn Guo, Bernhard Walle

On 03/28/2012 08:33 AM, 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>

> diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
> +Required properties:
> +  - compatible: "pwm-backlight"
> +  - pwms: OF device-tree PWM specification (see PWM binding[0])
> +  - num-brightness-levels: number of distinct brightness levels

Do we need the "num-brightness-levels" property at all; it's just the
length of the brightness-levels property.

> +  - 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).
...> +Example:
> +
> +	backlight {
> +		compatible = "pwm-backlight";
> +		pwms = <&pwm 0 5000000>;
> +
> +		num-brightness-levels = <8>;
> +		brightness-levels = <0 4 8 16 32 64 128 255>;
> +		default-brightness-level = <6>;
> +	};

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

* [PATCH v5 15/16] pwm-backlight: Add rudimentary device tree support
@ 2012-03-30 19:04       ` Stephen Warren
  0 siblings, 0 replies; 96+ messages in thread
From: Stephen Warren @ 2012-03-30 19:04 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/28/2012 08:33 AM, 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>

> diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
> +Required properties:
> +  - compatible: "pwm-backlight"
> +  - pwms: OF device-tree PWM specification (see PWM binding[0])
> +  - num-brightness-levels: number of distinct brightness levels

Do we need the "num-brightness-levels" property at all; it's just the
length of the brightness-levels property.

> +  - 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).
...> +Example:
> +
> +	backlight {
> +		compatible = "pwm-backlight";
> +		pwms = <&pwm 0 5000000>;
> +
> +		num-brightness-levels = <8>;
> +		brightness-levels = <0 4 8 16 32 64 128 255>;
> +		default-brightness-level = <6>;
> +	};

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

* Re: [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
  2012-03-30 10:38                   ` Thierry Reding
@ 2012-03-31 14:30                       ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-31 14:30 UTC (permalink / raw)
  To: Mark Brown
  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: 2990 bytes --]

* Thierry Reding wrote:
> * Mark Brown wrote:
> > On Fri, Mar 30, 2012 at 07:06:41AM +0200, Thierry Reding wrote:
> > > * Mark Brown wrote:
> > 
> > > > The clock and regulator APIs namespace the consumers by struct device -
> > > > might this not be sensible here?  pwm_get() does already take the device
> > > > as an argument.  It'd feel safer, and for example there's plenty of
> > > > phones out there with two backlit displays...
> > 
> > > That's actually how this is supposed to work. "pwm-backlight" in the above
> > > case is matched against the name of the struct device that you pass in to
> > > pwm_get(). The only difference, at least as far as I can tell, to the clock
> > > and regulator APIs is that a second name is not listed explicitly in the
> > > lookup table.
> > 
> > Both clock and regulator APIs map (source) -> (dev, name).  This only
> > has a mapping (source) -> (dev).
> > 
> > > So compared with the clock and regulator APIs it doesn't make too much sense
> > > to pass both the struct device and the name to pwm_get() because it will
> > > match the device name against the consumer name in the lookup table first and
> > > only use the passed name if no match was found.
> > 
> > This is different to what the clock and regulator APIs do - they look up
> > the name in the context of the struct device.  This is because...
> > 
> > > In case you have two backlight devices I would expect the following to work:
> > 
> > > 	static struct pwm_lookup board_pwm_lookup[] = {
> > > 		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight.0"),
> > > 		PWM_LOOKUP("tegra-pwm", 1, "pwm-backlight.1"),
> > > 	};
> > 
> > ...if a single device uses more than one PWM then the above scheme won't
> > work - unless I'm missing something?
> 
> Right, now it makes more sense. So basically if I have a single device using
> two PWM devices, then I'll need to have something like this:
> 
> 	static struct pwm_lookup board_pwm_lookup[] = {
> 		PWM_LOOKUP("tegra-pwm", 0, "pwm-foo.0", "bar"),
> 		PWM_LOOKUP("tegra-pwm", 1, "pwm-foo.0", "baz"),
> 	};
> 
> And then of course I'll need to have a second field by which the PWM device
> can be identified. Good, I'll fix that up for the next round.

I've been thinking about this some more, and I can see this becoming a little
problem with the DT bindings because the unified pwm_get() always requests
the first PWM specified in the "pwms" property.

The best solution that I could come up with is to not pass the index into the
of_pwm_request() function but rather forward the consumer name as passed into
pwm_get(). The of_pwm_request() could use the "pwm-names" property to do a
reverse lookup of the index and request that. One good thing about that would
be that I no longer need to export the of_pwm_request() function. The "bad"
thing is that it'll make the "pwm-names" property mandatory if more than a
single PWM is requested.

Does that sound reasonable?

Thierry

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

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
@ 2012-03-31 14:30                       ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-03-31 14:30 UTC (permalink / raw)
  To: linux-arm-kernel

* Thierry Reding wrote:
> * Mark Brown wrote:
> > On Fri, Mar 30, 2012 at 07:06:41AM +0200, Thierry Reding wrote:
> > > * Mark Brown wrote:
> > 
> > > > The clock and regulator APIs namespace the consumers by struct device -
> > > > might this not be sensible here?  pwm_get() does already take the device
> > > > as an argument.  It'd feel safer, and for example there's plenty of
> > > > phones out there with two backlit displays...
> > 
> > > That's actually how this is supposed to work. "pwm-backlight" in the above
> > > case is matched against the name of the struct device that you pass in to
> > > pwm_get(). The only difference, at least as far as I can tell, to the clock
> > > and regulator APIs is that a second name is not listed explicitly in the
> > > lookup table.
> > 
> > Both clock and regulator APIs map (source) -> (dev, name).  This only
> > has a mapping (source) -> (dev).
> > 
> > > So compared with the clock and regulator APIs it doesn't make too much sense
> > > to pass both the struct device and the name to pwm_get() because it will
> > > match the device name against the consumer name in the lookup table first and
> > > only use the passed name if no match was found.
> > 
> > This is different to what the clock and regulator APIs do - they look up
> > the name in the context of the struct device.  This is because...
> > 
> > > In case you have two backlight devices I would expect the following to work:
> > 
> > > 	static struct pwm_lookup board_pwm_lookup[] = {
> > > 		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight.0"),
> > > 		PWM_LOOKUP("tegra-pwm", 1, "pwm-backlight.1"),
> > > 	};
> > 
> > ...if a single device uses more than one PWM then the above scheme won't
> > work - unless I'm missing something?
> 
> Right, now it makes more sense. So basically if I have a single device using
> two PWM devices, then I'll need to have something like this:
> 
> 	static struct pwm_lookup board_pwm_lookup[] = {
> 		PWM_LOOKUP("tegra-pwm", 0, "pwm-foo.0", "bar"),
> 		PWM_LOOKUP("tegra-pwm", 1, "pwm-foo.0", "baz"),
> 	};
> 
> And then of course I'll need to have a second field by which the PWM device
> can be identified. Good, I'll fix that up for the next round.

I've been thinking about this some more, and I can see this becoming a little
problem with the DT bindings because the unified pwm_get() always requests
the first PWM specified in the "pwms" property.

The best solution that I could come up with is to not pass the index into the
of_pwm_request() function but rather forward the consumer name as passed into
pwm_get(). The of_pwm_request() could use the "pwm-names" property to do a
reverse lookup of the index and request that. One good thing about that would
be that I no longer need to export the of_pwm_request() function. The "bad"
thing is that it'll make the "pwm-names" property mandatory if more than a
single PWM is requested.

Does that sound reasonable?

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/20120331/c7eebdaf/attachment.sig>

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

* Re: [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
  2012-03-31 14:30                       ` Thierry Reding
@ 2012-04-01 15:20                           ` Shawn Guo
  -1 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-01 15:20 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Lars-Peter Clausen, Ryan Mallon,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Brown,
	Rob Herring, Colin Cross, Richard Purdie, Bernhard Walle,
	Matthias Kaehlcke, linux-tegra-u79uwXL29TY76Z2rM5mHXA, Eric Miao,
	Sascha Hauer, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Kurt Van Dijck

On Sat, Mar 31, 2012 at 04:30:21PM +0200, Thierry Reding wrote:
...
> The best solution that I could come up with is to not pass the index into the
> of_pwm_request() function but rather forward the consumer name as passed into
> pwm_get(). The of_pwm_request() could use the "pwm-names" property to do a
> reverse lookup of the index and request that. One good thing about that would
> be that I no longer need to export the of_pwm_request() function.

I never understand why of_pwm_request() needs to be exported anyway.
pwm_get() should be the only interface to pwm new/DT users, IMO.

> The "bad"
> thing is that it'll make the "pwm-names" property mandatory if more than a
> single PWM is requested.
> 
You do not have to make it mandatory, but anyone requesting any pwm
other than the first on in "pwms" list should fail.

> Does that sound reasonable?
> 
The above is exactly what clock DT does, so it sounds reasonable to me.

And I would expect the next version can just turn the second parameter
of pwm_get() into the one that will be used to identify the pwm device
rather than adding one more parameter.  Then we have pwm API well
aligned with clk and regulator.

-- 
Regards,
Shawn

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
@ 2012-04-01 15:20                           ` Shawn Guo
  0 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-01 15:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Mar 31, 2012 at 04:30:21PM +0200, Thierry Reding wrote:
...
> The best solution that I could come up with is to not pass the index into the
> of_pwm_request() function but rather forward the consumer name as passed into
> pwm_get(). The of_pwm_request() could use the "pwm-names" property to do a
> reverse lookup of the index and request that. One good thing about that would
> be that I no longer need to export the of_pwm_request() function.

I never understand why of_pwm_request() needs to be exported anyway.
pwm_get() should be the only interface to pwm new/DT users, IMO.

> The "bad"
> thing is that it'll make the "pwm-names" property mandatory if more than a
> single PWM is requested.
> 
You do not have to make it mandatory, but anyone requesting any pwm
other than the first on in "pwms" list should fail.

> Does that sound reasonable?
> 
The above is exactly what clock DT does, so it sounds reasonable to me.

And I would expect the next version can just turn the second parameter
of pwm_get() into the one that will be used to identify the pwm device
rather than adding one more parameter.  Then we have pwm API well
aligned with clk and regulator.

-- 
Regards,
Shawn

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

* Re: [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
  2012-04-01 15:20                           ` Shawn Guo
@ 2012-04-02  0:47                               ` Shawn Guo
  -1 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-02  0:47 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Mark Brown, 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 Sun, Apr 01, 2012 at 11:20:46PM +0800, Shawn Guo wrote:
> On Sat, Mar 31, 2012 at 04:30:21PM +0200, Thierry Reding wrote:
...
> > The "bad"
> > thing is that it'll make the "pwm-names" property mandatory if more than a
> > single PWM is requested.
> > 
> You do not have to make it mandatory, but anyone requesting any pwm
> other than the first on in "pwms" list should fail.
> 
I should have put it this way.  If a client device does not have
"pwm-names" property in its node, it should fail when requesting pwm
with a name.  And if no name is given, the first pwm device in the
"pwms" list could just be returned.

-- 
Regards,
Shawn

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
@ 2012-04-02  0:47                               ` Shawn Guo
  0 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-02  0:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Apr 01, 2012 at 11:20:46PM +0800, Shawn Guo wrote:
> On Sat, Mar 31, 2012 at 04:30:21PM +0200, Thierry Reding wrote:
...
> > The "bad"
> > thing is that it'll make the "pwm-names" property mandatory if more than a
> > single PWM is requested.
> > 
> You do not have to make it mandatory, but anyone requesting any pwm
> other than the first on in "pwms" list should fail.
> 
I should have put it this way.  If a client device does not have
"pwm-names" property in its node, it should fail when requesting pwm
with a name.  And if no name is given, the first pwm device in the
"pwms" list could just be returned.

-- 
Regards,
Shawn

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

* Re: [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
  2012-04-02  0:47                               ` Shawn Guo
@ 2012-04-02  4:50                                   ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-02  4:50 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Mark Brown, 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

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

* Shawn Guo wrote:
> On Sun, Apr 01, 2012 at 11:20:46PM +0800, Shawn Guo wrote:
> > On Sat, Mar 31, 2012 at 04:30:21PM +0200, Thierry Reding wrote:
> ...
> > > The "bad"
> > > thing is that it'll make the "pwm-names" property mandatory if more than a
> > > single PWM is requested.
> > > 
> > You do not have to make it mandatory, but anyone requesting any pwm
> > other than the first on in "pwms" list should fail.
> > 
> I should have put it this way.  If a client device does not have
> "pwm-names" property in its node, it should fail when requesting pwm
> with a name.  And if no name is given, the first pwm device in the
> "pwms" list could just be returned.

Yes, that's how I understood it. =)

Thierry

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

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

* [PATCH v5 04/16] pwm: Add table-based lookup for static mappings
@ 2012-04-02  4:50                                   ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-02  4:50 UTC (permalink / raw)
  To: linux-arm-kernel

* Shawn Guo wrote:
> On Sun, Apr 01, 2012 at 11:20:46PM +0800, Shawn Guo wrote:
> > On Sat, Mar 31, 2012 at 04:30:21PM +0200, Thierry Reding wrote:
> ...
> > > The "bad"
> > > thing is that it'll make the "pwm-names" property mandatory if more than a
> > > single PWM is requested.
> > > 
> > You do not have to make it mandatory, but anyone requesting any pwm
> > other than the first on in "pwms" list should fail.
> > 
> I should have put it this way.  If a client device does not have
> "pwm-names" property in its node, it should fail when requesting pwm
> with a name.  And if no name is given, the first pwm device in the
> "pwms" list could just be returned.

Yes, that's how I understood it. =)

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/20120402/b14048f2/attachment.sig>

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

* Re: [PATCH v5 09/16] pwm: tegra: Add device tree support
  2012-03-30 19:00         ` Stephen Warren
@ 2012-04-02  8:37             ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-02  8:37 UTC (permalink / raw)
  To: Stephen Warren
  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, 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: 1046 bytes --]

* Stephen Warren wrote:
> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> > 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>
> 
> > diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> ...
> > +#ifdef CONFIG_OF
> > +static struct of_device_id tegra_pwm_of_match[] = {
> > +	{ .compatible = "nvidia,tegra20-pwm" },
> > +	{ .compatible = "nvidia,tegra30-pwm" },
> 
> Could you swap those two lines, so that tegra30-pwm matches first. It
> makes no difference at present, but might in the future if the driver
> actually has to differentiate the two SoCs.

I thought the matching order was determined by the compatible property in the
device tree, not the OF match table of the driver.

Thierry

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

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
@ 2012-04-02  8:37             ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-02  8:37 UTC (permalink / raw)
  To: linux-arm-kernel

* Stephen Warren wrote:
> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> > 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>
> 
> > diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> ...
> > +#ifdef CONFIG_OF
> > +static struct of_device_id tegra_pwm_of_match[] = {
> > +	{ .compatible = "nvidia,tegra20-pwm" },
> > +	{ .compatible = "nvidia,tegra30-pwm" },
> 
> Could you swap those two lines, so that tegra30-pwm matches first. It
> makes no difference at present, but might in the future if the driver
> actually has to differentiate the two SoCs.

I thought the matching order was determined by the compatible property in the
device tree, not the OF match table of the driver.

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/20120402/066b6c5d/attachment.sig>

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

* Re: [PATCH v5 09/16] pwm: tegra: Add device tree support
  2012-04-02  8:37             ` Thierry Reding
@ 2012-04-02 15:42                 ` Stephen Warren
  -1 siblings, 0 replies; 96+ messages in thread
From: Stephen Warren @ 2012-04-02 15:42 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, Richard Purdie, Mark Brown,
	Mitch Bradley, Mike Frysinger, Eric Miao, Lars-Peter Clausen,
	Ryan Mallon, Shawn Guo, Bernhard Walle

On 04/02/2012 02:37 AM, Thierry Reding wrote:
> * Stephen Warren wrote:
>> On 03/28/2012 08:33 AM, Thierry Reding wrote:
>>> 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>
>>
>>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
>> ...
>>> +#ifdef CONFIG_OF
>>> +static struct of_device_id tegra_pwm_of_match[] = {
>>> +	{ .compatible = "nvidia,tegra20-pwm" },
>>> +	{ .compatible = "nvidia,tegra30-pwm" },
>>
>> Could you swap those two lines, so that tegra30-pwm matches first. It
>> makes no difference at present, but might in the future if the driver
>> actually has to differentiate the two SoCs.
> 
> I thought the matching order was determined by the compatible property in the
> device tree, not the OF match table of the driver.

At least logically, yes. However, of_match_device() appears to iterate
over each match table entry, checking whether it matches any string in
the compatible flag. Perhaps this could be considered a bug?

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
@ 2012-04-02 15:42                 ` Stephen Warren
  0 siblings, 0 replies; 96+ messages in thread
From: Stephen Warren @ 2012-04-02 15:42 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/02/2012 02:37 AM, Thierry Reding wrote:
> * Stephen Warren wrote:
>> On 03/28/2012 08:33 AM, Thierry Reding wrote:
>>> 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>
>>
>>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
>> ...
>>> +#ifdef CONFIG_OF
>>> +static struct of_device_id tegra_pwm_of_match[] = {
>>> +	{ .compatible = "nvidia,tegra20-pwm" },
>>> +	{ .compatible = "nvidia,tegra30-pwm" },
>>
>> Could you swap those two lines, so that tegra30-pwm matches first. It
>> makes no difference at present, but might in the future if the driver
>> actually has to differentiate the two SoCs.
> 
> I thought the matching order was determined by the compatible property in the
> device tree, not the OF match table of the driver.

At least logically, yes. However, of_match_device() appears to iterate
over each match table entry, checking whether it matches any string in
the compatible flag. Perhaps this could be considered a bug?

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

* Re: [PATCH v5 09/16] pwm: tegra: Add device tree support
  2012-04-02 15:42                 ` Stephen Warren
@ 2012-04-03 17:55                     ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-03 17:55 UTC (permalink / raw)
  To: Stephen Warren
  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, 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: 1462 bytes --]

* Stephen Warren wrote:
> On 04/02/2012 02:37 AM, Thierry Reding wrote:
> > * Stephen Warren wrote:
> >> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> >>> 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>
> >>
> >>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> >> ...
> >>> +#ifdef CONFIG_OF
> >>> +static struct of_device_id tegra_pwm_of_match[] = {
> >>> +	{ .compatible = "nvidia,tegra20-pwm" },
> >>> +	{ .compatible = "nvidia,tegra30-pwm" },
> >>
> >> Could you swap those two lines, so that tegra30-pwm matches first. It
> >> makes no difference at present, but might in the future if the driver
> >> actually has to differentiate the two SoCs.
> > 
> > I thought the matching order was determined by the compatible property in the
> > device tree, not the OF match table of the driver.
> 
> At least logically, yes. However, of_match_device() appears to iterate
> over each match table entry, checking whether it matches any string in
> the compatible flag. Perhaps this could be considered a bug?

It certainly is counter-intuitive. Maybe Grant or Rob can comment?

Thierry

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

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

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

* Stephen Warren wrote:
> On 04/02/2012 02:37 AM, Thierry Reding wrote:
> > * Stephen Warren wrote:
> >> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> >>> 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>
> >>
> >>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> >> ...
> >>> +#ifdef CONFIG_OF
> >>> +static struct of_device_id tegra_pwm_of_match[] = {
> >>> +	{ .compatible = "nvidia,tegra20-pwm" },
> >>> +	{ .compatible = "nvidia,tegra30-pwm" },
> >>
> >> Could you swap those two lines, so that tegra30-pwm matches first. It
> >> makes no difference at present, but might in the future if the driver
> >> actually has to differentiate the two SoCs.
> > 
> > I thought the matching order was determined by the compatible property in the
> > device tree, not the OF match table of the driver.
> 
> At least logically, yes. However, of_match_device() appears to iterate
> over each match table entry, checking whether it matches any string in
> the compatible flag. Perhaps this could be considered a bug?

It certainly is counter-intuitive. Maybe Grant or Rob can comment?

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/20120403/ecd4b896/attachment.sig>

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

* Re: [PATCH v5 09/16] pwm: tegra: Add device tree support
  2012-04-03 17:55                     ` Thierry Reding
@ 2012-04-03 19:43                         ` Rob Herring
  -1 siblings, 0 replies; 96+ messages in thread
From: Rob Herring @ 2012-04-03 19:43 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Stephen Warren, 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 04/03/2012 12:55 PM, Thierry Reding wrote:
> * Stephen Warren wrote:
>> On 04/02/2012 02:37 AM, Thierry Reding wrote:
>>> * Stephen Warren wrote:
>>>> On 03/28/2012 08:33 AM, Thierry Reding wrote:
>>>>> 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>
>>>>
>>>>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
>>>> ...
>>>>> +#ifdef CONFIG_OF
>>>>> +static struct of_device_id tegra_pwm_of_match[] = {
>>>>> +	{ .compatible = "nvidia,tegra20-pwm" },
>>>>> +	{ .compatible = "nvidia,tegra30-pwm" },
>>>>
>>>> Could you swap those two lines, so that tegra30-pwm matches first. It
>>>> makes no difference at present, but might in the future if the driver
>>>> actually has to differentiate the two SoCs.
>>>
>>> I thought the matching order was determined by the compatible property in the
>>> device tree, not the OF match table of the driver.
>>
>> At least logically, yes. However, of_match_device() appears to iterate
>> over each match table entry, checking whether it matches any string in
>> the compatible flag. Perhaps this could be considered a bug?
> 
> It certainly is counter-intuitive. Maybe Grant or Rob can comment?

I think the rule should be the match list is most specific to least
specific and/or mutually exclusive like compatible strings. Most
examples with multiple entries in the kernel seem to fall into the
mutually exclusive case.

For this case, I think would just drop tegra30-pwm altogether and only
add it to the driver when you need to distinguish between the 2. The dts
should have both though for Tegra3.

Rob

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
@ 2012-04-03 19:43                         ` Rob Herring
  0 siblings, 0 replies; 96+ messages in thread
From: Rob Herring @ 2012-04-03 19:43 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/03/2012 12:55 PM, Thierry Reding wrote:
> * Stephen Warren wrote:
>> On 04/02/2012 02:37 AM, Thierry Reding wrote:
>>> * Stephen Warren wrote:
>>>> On 03/28/2012 08:33 AM, Thierry Reding wrote:
>>>>> 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>
>>>>
>>>>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
>>>> ...
>>>>> +#ifdef CONFIG_OF
>>>>> +static struct of_device_id tegra_pwm_of_match[] = {
>>>>> +	{ .compatible = "nvidia,tegra20-pwm" },
>>>>> +	{ .compatible = "nvidia,tegra30-pwm" },
>>>>
>>>> Could you swap those two lines, so that tegra30-pwm matches first. It
>>>> makes no difference at present, but might in the future if the driver
>>>> actually has to differentiate the two SoCs.
>>>
>>> I thought the matching order was determined by the compatible property in the
>>> device tree, not the OF match table of the driver.
>>
>> At least logically, yes. However, of_match_device() appears to iterate
>> over each match table entry, checking whether it matches any string in
>> the compatible flag. Perhaps this could be considered a bug?
> 
> It certainly is counter-intuitive. Maybe Grant or Rob can comment?

I think the rule should be the match list is most specific to least
specific and/or mutually exclusive like compatible strings. Most
examples with multiple entries in the kernel seem to fall into the
mutually exclusive case.

For this case, I think would just drop tegra30-pwm altogether and only
add it to the driver when you need to distinguish between the 2. The dts
should have both though for Tegra3.

Rob

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

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

On Tue, 3 Apr 2012 19:55:11 +0200, Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org> wrote:
> * Stephen Warren wrote:
> > On 04/02/2012 02:37 AM, Thierry Reding wrote:
> > > * Stephen Warren wrote:
> > >> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> > >>> 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>
> > >>
> > >>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> > >> ...
> > >>> +#ifdef CONFIG_OF
> > >>> +static struct of_device_id tegra_pwm_of_match[] = {
> > >>> +	{ .compatible = "nvidia,tegra20-pwm" },
> > >>> +	{ .compatible = "nvidia,tegra30-pwm" },
> > >>
> > >> Could you swap those two lines, so that tegra30-pwm matches first. It
> > >> makes no difference at present, but might in the future if the driver
> > >> actually has to differentiate the two SoCs.
> > > 
> > > I thought the matching order was determined by the compatible property in the
> > > device tree, not the OF match table of the driver.
> > 
> > At least logically, yes. However, of_match_device() appears to iterate
> > over each match table entry, checking whether it matches any string in
> > the compatible flag. Perhaps this could be considered a bug?
> 
> It certainly is counter-intuitive. Maybe Grant or Rob can comment?

Yes, it is a bug.  The order of of_device_id should be entirely
irrelevant, and the order in the DT compatible property should
determine which match entry is returned.

g.

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
@ 2012-04-03 23:42                         ` Grant Likely
  0 siblings, 0 replies; 96+ messages in thread
From: Grant Likely @ 2012-04-03 23:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 3 Apr 2012 19:55:11 +0200, Thierry Reding <thierry.reding@avionic-design.de> wrote:
> * Stephen Warren wrote:
> > On 04/02/2012 02:37 AM, Thierry Reding wrote:
> > > * Stephen Warren wrote:
> > >> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> > >>> 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>
> > >>
> > >>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> > >> ...
> > >>> +#ifdef CONFIG_OF
> > >>> +static struct of_device_id tegra_pwm_of_match[] = {
> > >>> +	{ .compatible = "nvidia,tegra20-pwm" },
> > >>> +	{ .compatible = "nvidia,tegra30-pwm" },
> > >>
> > >> Could you swap those two lines, so that tegra30-pwm matches first. It
> > >> makes no difference at present, but might in the future if the driver
> > >> actually has to differentiate the two SoCs.
> > > 
> > > I thought the matching order was determined by the compatible property in the
> > > device tree, not the OF match table of the driver.
> > 
> > At least logically, yes. However, of_match_device() appears to iterate
> > over each match table entry, checking whether it matches any string in
> > the compatible flag. Perhaps this could be considered a bug?
> 
> It certainly is counter-intuitive. Maybe Grant or Rob can comment?

Yes, it is a bug.  The order of of_device_id should be entirely
irrelevant, and the order in the DT compatible property should
determine which match entry is returned.

g.

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

* Re: [PATCH v5 09/16] pwm: tegra: Add device tree support
  2012-04-03 23:42                         ` Grant Likely
@ 2012-04-04  5:00                           ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-04  5:00 UTC (permalink / raw)
  To: Grant Likely
  Cc: Stephen Warren, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Sascha Hauer, Arnd Bergmann,
	Matthias Kaehlcke, Kurt Van Dijck, Rob Herring, Colin Cross,
	Olof Johansson, 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: 2550 bytes --]

* Grant Likely wrote:
> On Tue, 3 Apr 2012 19:55:11 +0200, Thierry Reding <thierry.reding@avionic-design.de> wrote:
> > * Stephen Warren wrote:
> > > On 04/02/2012 02:37 AM, Thierry Reding wrote:
> > > > * Stephen Warren wrote:
> > > >> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> > > >>> 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>
> > > >>
> > > >>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> > > >> ...
> > > >>> +#ifdef CONFIG_OF
> > > >>> +static struct of_device_id tegra_pwm_of_match[] = {
> > > >>> +	{ .compatible = "nvidia,tegra20-pwm" },
> > > >>> +	{ .compatible = "nvidia,tegra30-pwm" },
> > > >>
> > > >> Could you swap those two lines, so that tegra30-pwm matches first. It
> > > >> makes no difference at present, but might in the future if the driver
> > > >> actually has to differentiate the two SoCs.
> > > > 
> > > > I thought the matching order was determined by the compatible property in the
> > > > device tree, not the OF match table of the driver.
> > > 
> > > At least logically, yes. However, of_match_device() appears to iterate
> > > over each match table entry, checking whether it matches any string in
> > > the compatible flag. Perhaps this could be considered a bug?
> > 
> > It certainly is counter-intuitive. Maybe Grant or Rob can comment?
> 
> Yes, it is a bug.  The order of of_device_id should be entirely
> irrelevant, and the order in the DT compatible property should
> determine which match entry is returned.

I've had a look at the code and it looks like a fix will not be entirely
trivial. I think moving out the compatible check out of the while loop in
of_match_node() and separately iterate over all strings in the compatible
property would be the easiest. That will also prioritize the compatible
match over matches by name and type but I think that's exactly what we
want. From a quick look it certainly seems like the large majority of
drivers match by compatible anyway.

Do you want me to prepare a patch or can you take care of it?

Stephen: Can I assume that you're fine with this Tegra PWM patch if such
a change to the matching function is committed?

Thierry

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

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
@ 2012-04-04  5:00                           ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-04  5:00 UTC (permalink / raw)
  To: linux-arm-kernel

* Grant Likely wrote:
> On Tue, 3 Apr 2012 19:55:11 +0200, Thierry Reding <thierry.reding@avionic-design.de> wrote:
> > * Stephen Warren wrote:
> > > On 04/02/2012 02:37 AM, Thierry Reding wrote:
> > > > * Stephen Warren wrote:
> > > >> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> > > >>> 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>
> > > >>
> > > >>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> > > >> ...
> > > >>> +#ifdef CONFIG_OF
> > > >>> +static struct of_device_id tegra_pwm_of_match[] = {
> > > >>> +	{ .compatible = "nvidia,tegra20-pwm" },
> > > >>> +	{ .compatible = "nvidia,tegra30-pwm" },
> > > >>
> > > >> Could you swap those two lines, so that tegra30-pwm matches first. It
> > > >> makes no difference at present, but might in the future if the driver
> > > >> actually has to differentiate the two SoCs.
> > > > 
> > > > I thought the matching order was determined by the compatible property in the
> > > > device tree, not the OF match table of the driver.
> > > 
> > > At least logically, yes. However, of_match_device() appears to iterate
> > > over each match table entry, checking whether it matches any string in
> > > the compatible flag. Perhaps this could be considered a bug?
> > 
> > It certainly is counter-intuitive. Maybe Grant or Rob can comment?
> 
> Yes, it is a bug.  The order of of_device_id should be entirely
> irrelevant, and the order in the DT compatible property should
> determine which match entry is returned.

I've had a look at the code and it looks like a fix will not be entirely
trivial. I think moving out the compatible check out of the while loop in
of_match_node() and separately iterate over all strings in the compatible
property would be the easiest. That will also prioritize the compatible
match over matches by name and type but I think that's exactly what we
want. From a quick look it certainly seems like the large majority of
drivers match by compatible anyway.

Do you want me to prepare a patch or can you take care of it?

Stephen: Can I assume that you're fine with this Tegra PWM patch if such
a change to the matching function is committed?

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/20120404/3379f66c/attachment-0001.sig>

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

* Re: [PATCH v5 01/16] pwm: Add PWM framework support
  2012-03-28 14:33     ` Thierry Reding
@ 2012-04-04  6:36         ` Shawn Guo
  -1 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-04  6:36 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, 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

A couple of trivial editorial errors, otherwise,

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

On Wed, Mar 28, 2012 at 04:33:43PM +0200, Thierry Reding wrote:
> 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,

s/The/There

> but it does not allow for multiple drivers as each of them implements the
> pwm_*() functions.
> 
...

> +/**
> + * pwm_request() - request a PWM device
> + * @pwm: global PWM device index

s/pwm/pwm_id

> + * @label: PWM device label
> + */
> +struct pwm_device *pwm_request(int pwm_id, const char *label)
...

> +/**
> + * struct pwm_chip - abstract a PWM
> + * @label: for diagnostics
> + * @owner: helps prevent removal of modules exporting active PWMs

The doc needs an update.

Regards,
Shawn

> + * @ops: The callbacks for this PWM
> + */
> +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.4
> 

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

* [PATCH v5 01/16] pwm: Add PWM framework support
@ 2012-04-04  6:36         ` Shawn Guo
  0 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-04  6:36 UTC (permalink / raw)
  To: linux-arm-kernel

A couple of trivial editorial errors, otherwise,

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

On Wed, Mar 28, 2012 at 04:33:43PM +0200, Thierry Reding wrote:
> 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,

s/The/There

> but it does not allow for multiple drivers as each of them implements the
> pwm_*() functions.
> 
...

> +/**
> + * pwm_request() - request a PWM device
> + * @pwm: global PWM device index

s/pwm/pwm_id

> + * @label: PWM device label
> + */
> +struct pwm_device *pwm_request(int pwm_id, const char *label)
...

> +/**
> + * struct pwm_chip - abstract a PWM
> + * @label: for diagnostics
> + * @owner: helps prevent removal of modules exporting active PWMs

The doc needs an update.

Regards,
Shawn

> + * @ops: The callbacks for this PWM
> + */
> +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.4
> 

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

* Re: [PATCH v5 01/16] pwm: Add PWM framework support
  2012-04-04  6:36         ` Shawn Guo
@ 2012-04-04  6:39             ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-04  6:39 UTC (permalink / raw)
  To: Shawn Guo
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, 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

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

* Shawn Guo wrote:
> A couple of trivial editorial errors, otherwise,
> 
> Reviewed-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> 
> On Wed, Mar 28, 2012 at 04:33:43PM +0200, Thierry Reding wrote:
> > 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,
> 
> s/The/There
> 
> > but it does not allow for multiple drivers as each of them implements the
> > pwm_*() functions.
> > 
> ...
> 
> > +/**
> > + * pwm_request() - request a PWM device
> > + * @pwm: global PWM device index
> 
> s/pwm/pwm_id
> 
> > + * @label: PWM device label
> > + */
> > +struct pwm_device *pwm_request(int pwm_id, const char *label)
> ...
> 
> > +/**
> > + * struct pwm_chip - abstract a PWM
> > + * @label: for diagnostics
> > + * @owner: helps prevent removal of modules exporting active PWMs
> 
> The doc needs an update.

Thanks,
Thierry

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

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

* [PATCH v5 01/16] pwm: Add PWM framework support
@ 2012-04-04  6:39             ` Thierry Reding
  0 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-04  6:39 UTC (permalink / raw)
  To: linux-arm-kernel

* Shawn Guo wrote:
> A couple of trivial editorial errors, otherwise,
> 
> Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
> 
> On Wed, Mar 28, 2012 at 04:33:43PM +0200, Thierry Reding wrote:
> > 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,
> 
> s/The/There
> 
> > but it does not allow for multiple drivers as each of them implements the
> > pwm_*() functions.
> > 
> ...
> 
> > +/**
> > + * pwm_request() - request a PWM device
> > + * @pwm: global PWM device index
> 
> s/pwm/pwm_id
> 
> > + * @label: PWM device label
> > + */
> > +struct pwm_device *pwm_request(int pwm_id, const char *label)
> ...
> 
> > +/**
> > + * struct pwm_chip - abstract a PWM
> > + * @label: for diagnostics
> > + * @owner: helps prevent removal of modules exporting active PWMs
> 
> The doc needs an update.

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/20120404/590c1526/attachment-0001.sig>

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

* Re: [PATCH v5 02/16] pwm: Allow chips to support multiple PWMs
  2012-03-28 14:33     ` Thierry Reding
@ 2012-04-04  6:44         ` Shawn Guo
  -1 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-04  6:44 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 Wed, Mar 28, 2012 at 04:33:44PM +0200, Thierry Reding wrote:
> 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: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

-- 
Regards,
Shawn

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

* [PATCH v5 02/16] pwm: Allow chips to support multiple PWMs
@ 2012-04-04  6:44         ` Shawn Guo
  0 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-04  6:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 28, 2012 at 04:33:44PM +0200, Thierry Reding wrote:
> 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: Shawn Guo <shawn.guo@linaro.org>

-- 
Regards,
Shawn

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

* Re: [PATCH v5 03/16] pwm: Add debugfs interface
  2012-03-28 14:33     ` Thierry Reding
@ 2012-04-04  6:47         ` Shawn Guo
  -1 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-04  6: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,
	Mark Brown, Mitch Bradley, Mike Frysinger, Eric Miao,
	Lars-Peter Clausen, Ryan Mallon, Bernhard Walle

On Wed, Mar 28, 2012 at 04:33:45PM +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: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

-- 
Regards,
Shawn

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

* [PATCH v5 03/16] pwm: Add debugfs interface
@ 2012-04-04  6:47         ` Shawn Guo
  0 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-04  6:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 28, 2012 at 04:33:45PM +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: Shawn Guo <shawn.guo@linaro.org>

-- 
Regards,
Shawn

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

* Re: [PATCH v5 08/16] pwm: Add NVIDIA Tegra SoC support
  2012-03-28 14:33     ` Thierry Reding
@ 2012-04-04  6:54         ` Shawn Guo
  -1 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-04  6:54 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 Wed, Mar 28, 2012 at 04:33:50PM +0200, Thierry Reding wrote:
...
> +static struct pwm_ops tegra_pwm_ops = {

const

> +	.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 -ENODEV;

The kernel doc of devm_request_and_ioremap() suggests -EADDRNOTAVAIL.

> +	}
> +

-- 
Regards,
Shawn

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

* [PATCH v5 08/16] pwm: Add NVIDIA Tegra SoC support
@ 2012-04-04  6:54         ` Shawn Guo
  0 siblings, 0 replies; 96+ messages in thread
From: Shawn Guo @ 2012-04-04  6:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 28, 2012 at 04:33:50PM +0200, Thierry Reding wrote:
...
> +static struct pwm_ops tegra_pwm_ops = {

const

> +	.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 -ENODEV;

The kernel doc of devm_request_and_ioremap() suggests -EADDRNOTAVAIL.

> +	}
> +

-- 
Regards,
Shawn

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

* Re: [PATCH v5 15/16] pwm-backlight: Add rudimentary device tree support
  2012-03-30 19:04       ` Stephen Warren
@ 2012-04-04 18:11           ` Thierry Reding
  -1 siblings, 0 replies; 96+ messages in thread
From: Thierry Reding @ 2012-04-04 18:11 UTC (permalink / raw)
  To: Stephen Warren
  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, 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: 988 bytes --]

* Stephen Warren wrote:
> On 03/28/2012 08:33 AM, 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>
> 
> > diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
> > +Required properties:
> > +  - compatible: "pwm-backlight"
> > +  - pwms: OF device-tree PWM specification (see PWM binding[0])
> > +  - num-brightness-levels: number of distinct brightness levels
> 
> Do we need the "num-brightness-levels" property at all; it's just the
> length of the brightness-levels property.

No, we don't really need it. As you say, it can be derived from the property
length.

Thierry

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

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

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

* Stephen Warren wrote:
> On 03/28/2012 08:33 AM, 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>
> 
> > diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight b/Documentation/devicetree/bindings/video/backlight/pwm-backlight
> > +Required properties:
> > +  - compatible: "pwm-backlight"
> > +  - pwms: OF device-tree PWM specification (see PWM binding[0])
> > +  - num-brightness-levels: number of distinct brightness levels
> 
> Do we need the "num-brightness-levels" property at all; it's just the
> length of the brightness-levels property.

No, we don't really need it. As you say, it can be derived from the property
length.

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/20120404/6c150550/attachment-0001.sig>

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

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

On 04/03/2012 11:00 PM, Thierry Reding wrote:
> * Grant Likely wrote:
>> On Tue, 3 Apr 2012 19:55:11 +0200, Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org> wrote:
>>> * Stephen Warren wrote:
>>>> On 04/02/2012 02:37 AM, Thierry Reding wrote:
>>>>> * Stephen Warren wrote:
>>>>>> On 03/28/2012 08:33 AM, Thierry Reding wrote:
>>>>>>> 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>
>>>>>>
>>>>>>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
>>>>>> ...
>>>>>>> +#ifdef CONFIG_OF
>>>>>>> +static struct of_device_id tegra_pwm_of_match[] = {
>>>>>>> +	{ .compatible = "nvidia,tegra20-pwm" },
>>>>>>> +	{ .compatible = "nvidia,tegra30-pwm" },
>>>>>>
>>>>>> Could you swap those two lines, so that tegra30-pwm matches first. It
>>>>>> makes no difference at present, but might in the future if the driver
>>>>>> actually has to differentiate the two SoCs.
>>>>>
>>>>> I thought the matching order was determined by the compatible property in the
>>>>> device tree, not the OF match table of the driver.
>>>>
>>>> At least logically, yes. However, of_match_device() appears to iterate
>>>> over each match table entry, checking whether it matches any string in
>>>> the compatible flag. Perhaps this could be considered a bug?
>>>
>>> It certainly is counter-intuitive. Maybe Grant or Rob can comment?
>>
>> Yes, it is a bug.  The order of of_device_id should be entirely
>> irrelevant, and the order in the DT compatible property should
>> determine which match entry is returned.
> 
> I've had a look at the code and it looks like a fix will not be entirely
> trivial. I think moving out the compatible check out of the while loop in
> of_match_node() and separately iterate over all strings in the compatible
> property would be the easiest. That will also prioritize the compatible
> match over matches by name and type but I think that's exactly what we
> want. From a quick look it certainly seems like the large majority of
> drivers match by compatible anyway.
> 
> Do you want me to prepare a patch or can you take care of it?
> 
> Stephen: Can I assume that you're fine with this Tegra PWM patch if such
> a change to the matching function is committed?

Yes.

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
@ 2012-04-04 18:32                               ` Stephen Warren
  0 siblings, 0 replies; 96+ messages in thread
From: Stephen Warren @ 2012-04-04 18:32 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/03/2012 11:00 PM, Thierry Reding wrote:
> * Grant Likely wrote:
>> On Tue, 3 Apr 2012 19:55:11 +0200, Thierry Reding <thierry.reding@avionic-design.de> wrote:
>>> * Stephen Warren wrote:
>>>> On 04/02/2012 02:37 AM, Thierry Reding wrote:
>>>>> * Stephen Warren wrote:
>>>>>> On 03/28/2012 08:33 AM, Thierry Reding wrote:
>>>>>>> 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>
>>>>>>
>>>>>>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
>>>>>> ...
>>>>>>> +#ifdef CONFIG_OF
>>>>>>> +static struct of_device_id tegra_pwm_of_match[] = {
>>>>>>> +	{ .compatible = "nvidia,tegra20-pwm" },
>>>>>>> +	{ .compatible = "nvidia,tegra30-pwm" },
>>>>>>
>>>>>> Could you swap those two lines, so that tegra30-pwm matches first. It
>>>>>> makes no difference at present, but might in the future if the driver
>>>>>> actually has to differentiate the two SoCs.
>>>>>
>>>>> I thought the matching order was determined by the compatible property in the
>>>>> device tree, not the OF match table of the driver.
>>>>
>>>> At least logically, yes. However, of_match_device() appears to iterate
>>>> over each match table entry, checking whether it matches any string in
>>>> the compatible flag. Perhaps this could be considered a bug?
>>>
>>> It certainly is counter-intuitive. Maybe Grant or Rob can comment?
>>
>> Yes, it is a bug.  The order of of_device_id should be entirely
>> irrelevant, and the order in the DT compatible property should
>> determine which match entry is returned.
> 
> I've had a look at the code and it looks like a fix will not be entirely
> trivial. I think moving out the compatible check out of the while loop in
> of_match_node() and separately iterate over all strings in the compatible
> property would be the easiest. That will also prioritize the compatible
> match over matches by name and type but I think that's exactly what we
> want. From a quick look it certainly seems like the large majority of
> drivers match by compatible anyway.
> 
> Do you want me to prepare a patch or can you take care of it?
> 
> Stephen: Can I assume that you're fine with this Tegra PWM patch if such
> a change to the matching function is committed?

Yes.

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

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

On Wed, 04 Apr 2012 12:32:04 -0600, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 04/03/2012 11:00 PM, Thierry Reding wrote:
> > * Grant Likely wrote:
> >> On Tue, 3 Apr 2012 19:55:11 +0200, Thierry Reding <thierry.reding@avionic-design.de> wrote:
> >>> * Stephen Warren wrote:
> >>>> On 04/02/2012 02:37 AM, Thierry Reding wrote:
> >>>>> * Stephen Warren wrote:
> >>>>>> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> >>>>>>> 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>
> >>>>>>
> >>>>>>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> >>>>>> ...
> >>>>>>> +#ifdef CONFIG_OF
> >>>>>>> +static struct of_device_id tegra_pwm_of_match[] = {
> >>>>>>> +	{ .compatible = "nvidia,tegra20-pwm" },
> >>>>>>> +	{ .compatible = "nvidia,tegra30-pwm" },
> >>>>>>
> >>>>>> Could you swap those two lines, so that tegra30-pwm matches first. It
> >>>>>> makes no difference at present, but might in the future if the driver
> >>>>>> actually has to differentiate the two SoCs.
> >>>>>
> >>>>> I thought the matching order was determined by the compatible property in the
> >>>>> device tree, not the OF match table of the driver.
> >>>>
> >>>> At least logically, yes. However, of_match_device() appears to iterate
> >>>> over each match table entry, checking whether it matches any string in
> >>>> the compatible flag. Perhaps this could be considered a bug?
> >>>
> >>> It certainly is counter-intuitive. Maybe Grant or Rob can comment?
> >>
> >> Yes, it is a bug.  The order of of_device_id should be entirely
> >> irrelevant, and the order in the DT compatible property should
> >> determine which match entry is returned.
> > 
> > I've had a look at the code and it looks like a fix will not be entirely
> > trivial. I think moving out the compatible check out of the while loop in
> > of_match_node() and separately iterate over all strings in the compatible
> > property would be the easiest. That will also prioritize the compatible
> > match over matches by name and type but I think that's exactly what we
> > want. From a quick look it certainly seems like the large majority of
> > drivers match by compatible anyway.
> > 
> > Do you want me to prepare a patch or can you take care of it?

yes please.

g.

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

* [PATCH v5 09/16] pwm: tegra: Add device tree support
@ 2012-04-07  1:44                                 ` Grant Likely
  0 siblings, 0 replies; 96+ messages in thread
From: Grant Likely @ 2012-04-07  1:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 04 Apr 2012 12:32:04 -0600, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 04/03/2012 11:00 PM, Thierry Reding wrote:
> > * Grant Likely wrote:
> >> On Tue, 3 Apr 2012 19:55:11 +0200, Thierry Reding <thierry.reding@avionic-design.de> wrote:
> >>> * Stephen Warren wrote:
> >>>> On 04/02/2012 02:37 AM, Thierry Reding wrote:
> >>>>> * Stephen Warren wrote:
> >>>>>> On 03/28/2012 08:33 AM, Thierry Reding wrote:
> >>>>>>> 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>
> >>>>>>
> >>>>>>> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
> >>>>>> ...
> >>>>>>> +#ifdef CONFIG_OF
> >>>>>>> +static struct of_device_id tegra_pwm_of_match[] = {
> >>>>>>> +	{ .compatible = "nvidia,tegra20-pwm" },
> >>>>>>> +	{ .compatible = "nvidia,tegra30-pwm" },
> >>>>>>
> >>>>>> Could you swap those two lines, so that tegra30-pwm matches first. It
> >>>>>> makes no difference at present, but might in the future if the driver
> >>>>>> actually has to differentiate the two SoCs.
> >>>>>
> >>>>> I thought the matching order was determined by the compatible property in the
> >>>>> device tree, not the OF match table of the driver.
> >>>>
> >>>> At least logically, yes. However, of_match_device() appears to iterate
> >>>> over each match table entry, checking whether it matches any string in
> >>>> the compatible flag. Perhaps this could be considered a bug?
> >>>
> >>> It certainly is counter-intuitive. Maybe Grant or Rob can comment?
> >>
> >> Yes, it is a bug.  The order of of_device_id should be entirely
> >> irrelevant, and the order in the DT compatible property should
> >> determine which match entry is returned.
> > 
> > I've had a look at the code and it looks like a fix will not be entirely
> > trivial. I think moving out the compatible check out of the while loop in
> > of_match_node() and separately iterate over all strings in the compatible
> > property would be the easiest. That will also prioritize the compatible
> > match over matches by name and type but I think that's exactly what we
> > want. From a quick look it certainly seems like the large majority of
> > drivers match by compatible anyway.
> > 
> > Do you want me to prepare a patch or can you take care of it?

yes please.

g.

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

end of thread, other threads:[~2012-04-07  1:44 UTC | newest]

Thread overview: 96+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-03-28 14:33 [PATCH v5 00/16] Add PWM framework and device tree support Thierry Reding
2012-03-28 14:33 ` Thierry Reding
     [not found] ` <1332945238-14897-1-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-03-28 14:33   ` [PATCH v5 01/16] pwm: Add PWM framework support Thierry Reding
2012-03-28 14:33     ` Thierry Reding
     [not found]     ` <1332945238-14897-2-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-03-29 21:41       ` Mark Brown
2012-03-29 21:41         ` Mark Brown
2012-04-04  6:36       ` Shawn Guo
2012-04-04  6:36         ` Shawn Guo
     [not found]         ` <20120404063605.GB7264-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-04-04  6:39           ` Thierry Reding
2012-04-04  6:39             ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 02/16] pwm: Allow chips to support multiple PWMs Thierry Reding
2012-03-28 14:33     ` Thierry Reding
     [not found]     ` <1332945238-14897-3-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-03-29 21:50       ` Mark Brown
2012-03-29 21:50         ` Mark Brown
2012-04-04  6:44       ` Shawn Guo
2012-04-04  6:44         ` Shawn Guo
2012-03-28 14:33   ` [PATCH v5 03/16] pwm: Add debugfs interface Thierry Reding
2012-03-28 14:33     ` Thierry Reding
     [not found]     ` <1332945238-14897-4-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-03-29 21:56       ` Mark Brown
2012-03-29 21:56         ` Mark Brown
2012-04-04  6:47       ` Shawn Guo
2012-04-04  6:47         ` Shawn Guo
2012-03-28 14:33   ` [PATCH v5 04/16] pwm: Add table-based lookup for static mappings Thierry Reding
2012-03-28 14:33     ` Thierry Reding
     [not found]     ` <1332945238-14897-5-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-03-29 22:03       ` Mark Brown
2012-03-29 22:03         ` Mark Brown
2012-03-30  5:06         ` Thierry Reding
2012-03-30  5:06           ` Thierry Reding
     [not found]           ` <20120330050641.GA21823-RM9K5IK7kjIQXX3q8xo1gnVAuStQJXxyR5q1nwbD4aMs9pC9oP6+/A@public.gmane.org>
2012-03-30 10:18             ` Mark Brown
2012-03-30 10:18               ` Mark Brown
     [not found]               ` <20120330101800.GD21950-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
2012-03-30 10:38                 ` Thierry Reding
2012-03-30 10:38                   ` Thierry Reding
     [not found]                   ` <20120330103840.GB961-RM9K5IK7kjIQXX3q8xo1gnVAuStQJXxyR5q1nwbD4aMs9pC9oP6+/A@public.gmane.org>
2012-03-31 14:30                     ` Thierry Reding
2012-03-31 14:30                       ` Thierry Reding
     [not found]                       ` <20120331143021.GA18382-RM9K5IK7kjIQXX3q8xo1gnVAuStQJXxyR5q1nwbD4aMs9pC9oP6+/A@public.gmane.org>
2012-04-01 15:20                         ` Shawn Guo
2012-04-01 15:20                           ` Shawn Guo
     [not found]                           ` <20120401152042.GE2395-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-04-02  0:47                             ` Shawn Guo
2012-04-02  0:47                               ` Shawn Guo
     [not found]                               ` <20120402004730.GA910-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2012-04-02  4:50                                 ` Thierry Reding
2012-04-02  4:50                                   ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 05/16] pwm: Add device tree support Thierry Reding
2012-03-28 14:33     ` Thierry Reding
     [not found]     ` <1332945238-14897-6-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-03-28 14:53       ` Arnd Bergmann
2012-03-28 14:53         ` Arnd Bergmann
2012-03-29 21:47       ` Mark Brown
2012-03-29 21:47         ` Mark Brown
     [not found]         ` <20120329214717.GG4153-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
2012-03-30  6:24           ` Thierry Reding
2012-03-30  6:24             ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 06/16] ARM: tegra: Fix PWM clock programming Thierry Reding
2012-03-28 14:33     ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 07/16] ARM: tegra: Provide clock for only one PWM controller Thierry Reding
2012-03-28 14:33     ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 08/16] pwm: Add NVIDIA Tegra SoC support Thierry Reding
2012-03-28 14:33     ` Thierry Reding
     [not found]     ` <1332945238-14897-9-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-03-30 18:57       ` Stephen Warren
2012-03-30 18:57         ` Stephen Warren
2012-04-04  6:54       ` Shawn Guo
2012-04-04  6:54         ` Shawn Guo
2012-03-28 14:33   ` [PATCH v5 09/16] pwm: tegra: Add device tree support Thierry Reding
2012-03-28 14:33     ` Thierry Reding
     [not found]     ` <1332945238-14897-10-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-03-30 19:00       ` Stephen Warren
2012-03-30 19:00         ` Stephen Warren
     [not found]         ` <4F7602CD.2010808-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2012-04-02  8:37           ` Thierry Reding
2012-04-02  8:37             ` Thierry Reding
     [not found]             ` <20120402083749.GA6576-RM9K5IK7kjIyiCvfTdI0JKcOhU4Rzj621B7CTYaBSLdn68oJJulU0Q@public.gmane.org>
2012-04-02 15:42               ` Stephen Warren
2012-04-02 15:42                 ` Stephen Warren
     [not found]                 ` <4F79C8D4.2000306-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2012-04-03 17:55                   ` Thierry Reding
2012-04-03 17:55                     ` Thierry Reding
     [not found]                     ` <20120403175511.GA26399-RM9K5IK7kjIQXX3q8xo1gnVAuStQJXxyR5q1nwbD4aMs9pC9oP6+/A@public.gmane.org>
2012-04-03 19:43                       ` Rob Herring
2012-04-03 19:43                         ` Rob Herring
2012-04-03 23:42                       ` Grant Likely
2012-04-03 23:42                         ` Grant Likely
2012-04-04  5:00                         ` Thierry Reding
2012-04-04  5:00                           ` Thierry Reding
     [not found]                           ` <20120404050054.GA30807-RM9K5IK7kjIyiCvfTdI0JKcOhU4Rzj621B7CTYaBSLdn68oJJulU0Q@public.gmane.org>
2012-04-04 18:32                             ` Stephen Warren
2012-04-04 18:32                               ` Stephen Warren
2012-04-07  1:44                               ` Grant Likely
2012-04-07  1:44                                 ` Grant Likely
2012-03-28 14:33   ` [PATCH v5 10/16] pwm: Move Blackfin PWM driver to PWM framework Thierry Reding
2012-03-28 14:33     ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 11/16] pwm: Move PXA " Thierry Reding
2012-03-28 14:33     ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 12/16] ARM i.MX: Move i.MX pwm driver to pwm framework Thierry Reding
2012-03-28 14:33     ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 13/16] ARM Samsung: Move s3c " Thierry Reding
2012-03-28 14:33     ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 14/16] ARM vt8500: Move vt8500 " Thierry Reding
2012-03-28 14:33     ` Thierry Reding
2012-03-28 14:33   ` [PATCH v5 16/16] pwm: Take over maintainership of the PWM subsystem Thierry Reding
2012-03-28 14:33     ` Thierry Reding
2012-03-28 14:33 ` [PATCH v5 15/16] pwm-backlight: Add rudimentary device tree support Thierry Reding
2012-03-28 14:33   ` Thierry Reding
     [not found]   ` <1332945238-14897-16-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-03-30 19:04     ` Stephen Warren
2012-03-30 19:04       ` Stephen Warren
     [not found]       ` <4F7603BC.2050300-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2012-04-04 18:11         ` Thierry Reding
2012-04-04 18:11           ` Thierry Reding

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.