261 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
                       Generic PWM Device API
 | 
						|
 | 
						|
                          February 1, 2010
 | 
						|
                            Bill Gatliff
 | 
						|
                        <bgat@billgatliff.com>
 | 
						|
 | 
						|
 | 
						|
 | 
						|
The code in drivers/pwm and include/linux/pwm/ implements an API for
 | 
						|
applications involving pulse-width-modulation signals.  This document
 | 
						|
describes how the API implementation facilitates both PWM-generating
 | 
						|
devices, and users of those devices.
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Motivation
 | 
						|
 | 
						|
The primary goals for implementing the "generic PWM API" are to
 | 
						|
consolidate the various PWM implementations within a consistent and
 | 
						|
redundancy-reducing framework, and to facilitate the use of
 | 
						|
hotpluggable PWM devices.
 | 
						|
 | 
						|
Previous PWM-related implementations within the Linux kernel achieved
 | 
						|
their consistency via cut-and-paste, but did not need to (and didn't)
 | 
						|
facilitate more than one PWM-generating device within the system---
 | 
						|
hotplug or otherwise.  The Generic PWM Device API might be most
 | 
						|
appropriately viewed as an update to those implementations, rather
 | 
						|
than a complete rewrite.
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Challenges
 | 
						|
 | 
						|
One of the difficulties in implementing a generic PWM framework is the
 | 
						|
fact that pulse-width-modulation applications involve real-world
 | 
						|
signals, which often must be carefully managed to prevent destruction
 | 
						|
of hardware that is linked to those signals.  A DC motor that
 | 
						|
experiences a brief interruption in the PWM signal controlling it
 | 
						|
might destructively overheat; it could suddenly change speed, losing
 | 
						|
synchronization with a sensor; it could even suddenly change direction
 | 
						|
or torque, breaking the mechanical device connected to it.
 | 
						|
 | 
						|
(A generic PWM device framework is not directly responsible for
 | 
						|
preventing the above scenarios: that responsibility lies with the
 | 
						|
hardware designer, and the application and driver authors.  But it
 | 
						|
must to the greatest extent possible make it easy to avoid such
 | 
						|
problems).
 | 
						|
 | 
						|
A generic PWM device framework must accommodate the substantial
 | 
						|
differences between available PWM-generating hardware devices, without
 | 
						|
becoming sub-optimal for any of them.
 | 
						|
 | 
						|
Finally, a generic PWM device framework must be relatively
 | 
						|
lightweight, computationally speaking.  Some PWM users demand
 | 
						|
high-speed outputs, plus the ability to regulate those outputs
 | 
						|
quickly.  A device framework must be able to "keep up" with such
 | 
						|
hardware, while still leaving time to do real work.
 | 
						|
 | 
						|
The Generic PWM Device API is an attempt to meet all of the above
 | 
						|
requirements.  At its initial publication, the API was already in use
 | 
						|
managing small DC motors, sensors and solenoids through a
 | 
						|
custom-designed, optically-isolated H-bridge driver.
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Functional Overview
 | 
						|
 | 
						|
The Generic PWM Device API framework is implemented in
 | 
						|
include/linux/pwm/pwm.h and drivers/pwm/pwm.c.  The functions therein
 | 
						|
use information from pwm_device, pwm_channel and pwm_channel_config
 | 
						|
structures to invoke services in PWM peripheral device drivers.
 | 
						|
Consult drivers/pwm/atmel-pwm.c for an example driver.
 | 
						|
 | 
						|
There are two classes of adopters of the PWM framework:
 | 
						|
 | 
						|
  "Users" -- those wishing to employ the API merely to produce PWM
 | 
						|
  signals; once they have identified the appropriate physical output
 | 
						|
  on the platform in question, they don't care about the details of
 | 
						|
  the underlying hardware
 | 
						|
 | 
						|
  "Driver authors" -- those wishing to bind devices that can generate
 | 
						|
  PWM signals to the Generic PWM Device API, so that the services of
 | 
						|
  those devices become available to users. Assuming the hardware can
 | 
						|
  support the needs of a user, driver authors don't care about the
 | 
						|
  details of the user's application
 | 
						|
 | 
						|
Generally speaking, users will first invoke pwm_request() to obtain a
 | 
						|
handle to a PWM device.  They will then pass that handle to functions
 | 
						|
like pwm_duty_ns() and pwm_period_ns() to set the duty cycle and
 | 
						|
period of the PWM signal, respectively.  They will also invoke
 | 
						|
pwm_start() and pwm_stop() to turn the signal on and off.
 | 
						|
 | 
						|
The Generic PWM API framework also provides a sysfs interface to PWM
 | 
						|
devices, which is adequate for basic application needs and testing.
 | 
						|
 | 
						|
Driver authors fill out a pwm_device structure, which describes the
 | 
						|
capabilities of the PWM hardware being constructed--- including the
 | 
						|
number of distinct output "channels" the peripheral offers.  They then
 | 
						|
invoke pwm_register() (usually from within their device's probe()
 | 
						|
handler) to make the PWM API aware of their device.  The framework
 | 
						|
will call back to the methods described in the pwm_device structure as
 | 
						|
users begin to configure and utilize the hardware.
 | 
						|
 | 
						|
Note that PWM signals can be produced by a variety of peripherals,
 | 
						|
beyond the true "PWM hardware" offered by many system-on-chip devices.
 | 
						|
Other possibilities include timer/counters with compare-match
 | 
						|
capabilities, carefully-programmed synchronous serial ports
 | 
						|
(e.g. SPI), and GPIO pins driven by kernel interval timers.  With a
 | 
						|
proper pwm_device structure, these devices and pseudo-devices can all
 | 
						|
be accommodated by the Generic PWM Device API framework.
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Using the API to Generate PWM Signals -- Basic Functions for Users
 | 
						|
 | 
						|
 | 
						|
pwm_request() -- Returns a pwm_channel pointer, which is subsequently
 | 
						|
passed to the other user-related PWM functions.  Once requested, a PWM
 | 
						|
channel is marked as in-use and subsequent requests prior to
 | 
						|
pwm_free() will fail.
 | 
						|
 | 
						|
The names used to refer to PWM devices are defined by driver authors.
 | 
						|
Typically they are platform device bus identifiers, and this
 | 
						|
convention is encouraged for consistency.
 | 
						|
 | 
						|
 | 
						|
pwm_free() -- Marks a PWM channel as no longer in use.  The PWM device
 | 
						|
is stopped before it is released by the API.
 | 
						|
 | 
						|
 | 
						|
pwm_period_ns() -- Specifies the PWM signal's period, in nanoseconds.
 | 
						|
 | 
						|
 | 
						|
pwm_duty_ns() -- Specifies the PWM signal's active duration, in nanoseconds.
 | 
						|
 | 
						|
 | 
						|
pwm_duty_percent() -- Specifies the PWM signal's active duration, as a
 | 
						|
percentage of the current period of the signal.  NOTE: this value is
 | 
						|
not recalculated if the period of the signal is subsequently changed.
 | 
						|
 | 
						|
 | 
						|
pwm_start(), pwm_stop() -- Turns the PWM signal on and off.  Except
 | 
						|
where stated otherwise by a driver author, signals are stopped at the
 | 
						|
end of the current period, at which time the output is set to its
 | 
						|
inactive state.
 | 
						|
 | 
						|
 | 
						|
pwm_polarity() -- Defines whether the PWM signal output's active
 | 
						|
region is "1" or "0".  A 10% duty-cycle, polarity=1 signal will
 | 
						|
conventionally be at 5V (or 3.3V, or 1000V, or whatever the platform
 | 
						|
hardware does) for 10% of the period.  The same configuration of a
 | 
						|
polarity=0 signal will be at 5V (or 3.3V, or ...) for 90% of the
 | 
						|
period.
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Using the API to Generate PWM Signals -- Advanced Functions
 | 
						|
 | 
						|
 | 
						|
pwm_config() -- Passes a pwm_channel_config structure to the
 | 
						|
associated device driver.  This function is invoked by pwm_start(),
 | 
						|
pwm_duty_ns(), etc. and is one of two main entry points to the PWM
 | 
						|
driver for the hardware being used.  The configuration change is
 | 
						|
guaranteed atomic if multiple configuration changes are specified.
 | 
						|
This function might sleep, depending on what the device driver has to
 | 
						|
do to satisfy the request.  All PWM device drivers must support this
 | 
						|
entry point.
 | 
						|
 | 
						|
 | 
						|
pwm_config_nosleep() -- Passes a pwm_channel_config structure to the
 | 
						|
associated device driver.  If the driver must sleep in order to
 | 
						|
implement the requested configuration change, -EWOULDBLOCK is
 | 
						|
returned.  Users may call this function from interrupt handlers, for
 | 
						|
example.  This is the other main entry point into the PWM hardware
 | 
						|
driver, but not all device drivers support this entry point.
 | 
						|
 | 
						|
 | 
						|
pwm_synchronize(), pwm_unsynchronize() -- "Synchronizes" two or more
 | 
						|
PWM channels, if the underlying hardware permits.  (If it doesn't, the
 | 
						|
framework facilitates emulating this capability but it is not yet
 | 
						|
implemented).  Synchronized channels will start and stop
 | 
						|
simultaneously when any single channel in the group is started or
 | 
						|
stopped.  Use pwm_unsynchronize(..., NULL) to completely detach a
 | 
						|
channel from any other synchronized channels.  By default, all PWM
 | 
						|
channels are unsynchronized.
 | 
						|
 | 
						|
 | 
						|
pwm_set_handler() -- Defines an end-of-period callback.  The indicated
 | 
						|
function will be invoked in a worker thread at the end of each PWM
 | 
						|
period, and can subsequently invoke pwm_config(), etc.  Must be used
 | 
						|
with extreme care for high-speed PWM outputs.  Set the handler
 | 
						|
function to NULL to un-set the handler.
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Implementing a PWM Device API Driver -- Functions for Driver Authors
 | 
						|
 | 
						|
 | 
						|
Fill out the appropriate fields in a pwm_device structure, and submit
 | 
						|
to pwm_register():
 | 
						|
 | 
						|
 | 
						|
bus_id -- the plain-text name of the device.  Users will bind to a
 | 
						|
channel on the device using this name plus the channel number.  For
 | 
						|
example, the Atmel PWMC's bus_id is "atmel_pwmc", the same as used by
 | 
						|
the platform device driver (recommended).  The first device registered
 | 
						|
thereby receives bus_id "atmel_pwmc.0", which is what you put in
 | 
						|
pwm_device.bus_id.  Channels are then named "atmel_pwmc.0:[0-3]".
 | 
						|
(Hint: just use pdev->dev.bus_id in your probe() method).
 | 
						|
 | 
						|
 | 
						|
nchan -- the number of distinct output channels provided by the device.
 | 
						|
 | 
						|
 | 
						|
request -- (optional) Invoked each time a user requests a channel.
 | 
						|
Use to turn on clocks, clean up register states, etc.  The framework
 | 
						|
takes care of device locking/unlocking; you will see only successful
 | 
						|
requests.
 | 
						|
 | 
						|
 | 
						|
free -- (optional) Callback for each time a user relinquishes a
 | 
						|
channel.  The framework will have already stopped, unsynchronized and
 | 
						|
un-handled the channel.  Use to turn off clocks, etc. as necessary.
 | 
						|
 | 
						|
 | 
						|
synchronize, unsynchronize -- (optional) Callbacks to
 | 
						|
synchronize/unsynchronize channels.  Some devices provide this
 | 
						|
capability in hardware; for others, it can be emulated (see
 | 
						|
atmel_pwmc.c's sync_mask for an example).
 | 
						|
 | 
						|
 | 
						|
set_callback -- (optional) Invoked when a user requests a handler.  If
 | 
						|
the hardware supports an end-of-period interrupt, invoke the function
 | 
						|
indicated during your interrupt handler.  The callback function itself
 | 
						|
is always internal to the API, and does not map directly to the user's
 | 
						|
callback function.
 | 
						|
 | 
						|
 | 
						|
config -- Invoked to change the device configuration, always from a
 | 
						|
sleep-capable context.  All the changes indicated must be performed
 | 
						|
atomically, ideally synchronized to an end-of-period event (so that
 | 
						|
you avoid short or long output pulses).  You may sleep, etc. as
 | 
						|
necessary within this function.
 | 
						|
 | 
						|
 | 
						|
config_nosleep -- (optional) Invoked to change device configuration
 | 
						|
from within a context that is not allowed to sleep.  If you cannot
 | 
						|
perform the requested configuration changes without sleeping, return
 | 
						|
-EWOULDBLOCK.
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Acknowledgements
 | 
						|
 | 
						|
 | 
						|
The author expresses his gratitude to the countless developers who
 | 
						|
have reviewed and submitted feedback on the various versions of the
 | 
						|
Generic PWM Device API code, and those who have submitted drivers and
 | 
						|
applications that use the framework.  You know who you are.  ;)
 | 
						|
 |