Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
18c58878
Commit
18c58878
authored
May 17, 2016
by
Thierry Reding
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-4.7/pwm-atomic' into for-next
parents
d2a3f206
23e3523f
Changes
22
Show whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
580 additions
and
213 deletions
+580
-213
Documentation/pwm.txt
Documentation/pwm.txt
+28
-2
arch/arm/mach-s3c24xx/mach-rx1950.c
arch/arm/mach-s3c24xx/mach-rx1950.c
+6
-0
drivers/clk/clk-pwm.c
drivers/clk/clk-pwm.c
+12
-5
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_panel.c
+6
-0
drivers/hwmon/pwm-fan.c
drivers/hwmon/pwm-fan.c
+20
-6
drivers/input/misc/max77693-haptic.c
drivers/input/misc/max77693-haptic.c
+14
-3
drivers/input/misc/max8997_haptic.c
drivers/input/misc/max8997_haptic.c
+6
-0
drivers/input/misc/pwm-beeper.c
drivers/input/misc/pwm-beeper.c
+6
-0
drivers/leds/leds-pwm.c
drivers/leds/leds-pwm.c
+10
-1
drivers/pwm/core.c
drivers/pwm/core.c
+132
-90
drivers/pwm/pwm-crc.c
drivers/pwm/pwm-crc.c
+1
-1
drivers/pwm/pwm-lpc18xx-sct.c
drivers/pwm/pwm-lpc18xx-sct.c
+1
-1
drivers/pwm/pwm-omap-dmtimer.c
drivers/pwm/pwm-omap-dmtimer.c
+1
-1
drivers/pwm/pwm-rcar.c
drivers/pwm/pwm-rcar.c
+1
-1
drivers/pwm/pwm-sun4i.c
drivers/pwm/pwm-sun4i.c
+2
-1
drivers/pwm/sysfs.c
drivers/pwm/sysfs.c
+56
-14
drivers/video/backlight/lm3630a_bl.c
drivers/video/backlight/lm3630a_bl.c
+7
-2
drivers/video/backlight/lp855x_bl.c
drivers/video/backlight/lp855x_bl.c
+6
-0
drivers/video/backlight/lp8788_bl.c
drivers/video/backlight/lp8788_bl.c
+6
-0
drivers/video/backlight/pwm_bl.c
drivers/video/backlight/pwm_bl.c
+10
-4
drivers/video/fbdev/ssd1307fb.c
drivers/video/fbdev/ssd1307fb.c
+10
-1
include/linux/pwm.h
include/linux/pwm.h
+239
-80
No files found.
Documentation/pwm.txt
View file @
18c58878
...
...
@@ -42,9 +42,26 @@ variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist.
After being requested, a PWM has to be configured using:
int pwm_
config(struct pwm_device *pwm, int duty_ns, int period_ns
);
int pwm_
apply_state(struct pwm_device *pwm, struct pwm_state *state
);
To start/stop toggling the PWM output use pwm_enable()/pwm_disable().
This API controls both the PWM period/duty_cycle config and the
enable/disable state.
The pwm_config(), pwm_enable() and pwm_disable() functions are just wrappers
around pwm_apply_state() and should not be used if the user wants to change
several parameter at once. For example, if you see pwm_config() and
pwm_{enable,disable}() calls in the same function, this probably means you
should switch to pwm_apply_state().
The PWM user API also allows one to query the PWM state with pwm_get_state().
In addition to the PWM state, the PWM API also exposes PWM arguments, which
are the reference PWM config one should use on this PWM.
PWM arguments are usually platform-specific and allows the PWM user to only
care about dutycycle relatively to the full period (like, duty = 50% of the
period). struct pwm_args contains 2 fields (period and polarity) and should
be used to set the initial PWM config (usually done in the probe function
of the PWM user). PWM arguments are retrieved with pwm_get_args().
Using PWMs with the sysfs interface
-----------------------------------
...
...
@@ -105,6 +122,15 @@ goes low for the remainder of the period. Conversely, a signal with inversed
polarity starts low for the duration of the duty cycle and goes high for the
remainder of the period.
Drivers are encouraged to implement ->apply() instead of the legacy
->enable(), ->disable() and ->config() methods. Doing that should provide
atomicity in the PWM config workflow, which is required when the PWM controls
a critical device (like a regulator).
The implementation of ->get_state() (a method used to retrieve initial PWM
state) is also encouraged for the same reason: letting the PWM user know
about the current PWM state would allow him to avoid glitches.
Locking
-------
...
...
arch/arm/mach-s3c24xx/mach-rx1950.c
View file @
18c58878
...
...
@@ -496,6 +496,12 @@ static int rx1950_backlight_init(struct device *dev)
return
PTR_ERR
(
lcd_pwm
);
}
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args
(
lcd_pwm
);
rx1950_lcd_power
(
1
);
rx1950_bl_power
(
1
);
...
...
drivers/clk/clk-pwm.c
View file @
18c58878
...
...
@@ -59,6 +59,7 @@ static int clk_pwm_probe(struct platform_device *pdev)
struct
clk_init_data
init
;
struct
clk_pwm
*
clk_pwm
;
struct
pwm_device
*
pwm
;
struct
pwm_args
pargs
;
const
char
*
clk_name
;
struct
clk
*
clk
;
int
ret
;
...
...
@@ -71,22 +72,28 @@ static int clk_pwm_probe(struct platform_device *pdev)
if
(
IS_ERR
(
pwm
))
return
PTR_ERR
(
pwm
);
if
(
!
pwm
->
period
)
{
pwm_get_args
(
pwm
,
&
pargs
);
if
(
!
pargs
.
period
)
{
dev_err
(
&
pdev
->
dev
,
"invalid PWM period
\n
"
);
return
-
EINVAL
;
}
if
(
of_property_read_u32
(
node
,
"clock-frequency"
,
&
clk_pwm
->
fixed_rate
))
clk_pwm
->
fixed_rate
=
NSEC_PER_SEC
/
p
wm
->
period
;
clk_pwm
->
fixed_rate
=
NSEC_PER_SEC
/
p
args
.
period
;
if
(
p
wm
->
period
!=
NSEC_PER_SEC
/
clk_pwm
->
fixed_rate
&&
p
wm
->
period
!=
DIV_ROUND_UP
(
NSEC_PER_SEC
,
clk_pwm
->
fixed_rate
))
{
if
(
p
args
.
period
!=
NSEC_PER_SEC
/
clk_pwm
->
fixed_rate
&&
p
args
.
period
!=
DIV_ROUND_UP
(
NSEC_PER_SEC
,
clk_pwm
->
fixed_rate
))
{
dev_err
(
&
pdev
->
dev
,
"clock-frequency does not match PWM period
\n
"
);
return
-
EINVAL
;
}
ret
=
pwm_config
(
pwm
,
(
pwm
->
period
+
1
)
>>
1
,
pwm
->
period
);
/*
* FIXME: pwm_apply_args() should be removed when switching to the
* atomic PWM API.
*/
pwm_apply_args
(
pwm
);
ret
=
pwm_config
(
pwm
,
(
pargs
.
period
+
1
)
>>
1
,
pargs
.
period
);
if
(
ret
<
0
)
return
ret
;
...
...
drivers/gpu/drm/i915/intel_panel.c
View file @
18c58878
...
...
@@ -1640,6 +1640,12 @@ static int pwm_setup_backlight(struct intel_connector *connector,
return
-
ENODEV
;
}
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args
(
panel
->
backlight
.
pwm
);
retval
=
pwm_config
(
panel
->
backlight
.
pwm
,
CRC_PMIC_PWM_PERIOD_NS
,
CRC_PMIC_PWM_PERIOD_NS
);
if
(
retval
<
0
)
{
...
...
drivers/hwmon/pwm-fan.c
View file @
18c58878
...
...
@@ -40,15 +40,18 @@ struct pwm_fan_ctx {
static
int
__set_pwm
(
struct
pwm_fan_ctx
*
ctx
,
unsigned
long
pwm
)
{
struct
pwm_args
pargs
;
unsigned
long
duty
;
int
ret
=
0
;
pwm_get_args
(
ctx
->
pwm
,
&
pargs
);
mutex_lock
(
&
ctx
->
lock
);
if
(
ctx
->
pwm_value
==
pwm
)
goto
exit_set_pwm_err
;
duty
=
DIV_ROUND_UP
(
pwm
*
(
ctx
->
pwm
->
period
-
1
),
MAX_PWM
);
ret
=
pwm_config
(
ctx
->
pwm
,
duty
,
ctx
->
pwm
->
period
);
duty
=
DIV_ROUND_UP
(
pwm
*
(
pargs
.
period
-
1
),
MAX_PWM
);
ret
=
pwm_config
(
ctx
->
pwm
,
duty
,
pargs
.
period
);
if
(
ret
)
goto
exit_set_pwm_err
;
...
...
@@ -215,6 +218,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
{
struct
thermal_cooling_device
*
cdev
;
struct
pwm_fan_ctx
*
ctx
;
struct
pwm_args
pargs
;
struct
device
*
hwmon
;
int
duty_cycle
;
int
ret
;
...
...
@@ -233,11 +237,19 @@ static int pwm_fan_probe(struct platform_device *pdev)
platform_set_drvdata
(
pdev
,
ctx
);
/*
* FIXME: pwm_apply_args() should be removed when switching to the
* atomic PWM API.
*/
pwm_apply_args
(
ctx
->
pwm
);
/* Set duty cycle to maximum allowed */
duty_cycle
=
ctx
->
pwm
->
period
-
1
;
pwm_get_args
(
ctx
->
pwm
,
&
pargs
);
duty_cycle
=
pargs
.
period
-
1
;
ctx
->
pwm_value
=
MAX_PWM
;
ret
=
pwm_config
(
ctx
->
pwm
,
duty_cycle
,
ctx
->
pwm
->
period
);
ret
=
pwm_config
(
ctx
->
pwm
,
duty_cycle
,
pargs
.
period
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"Failed to configure PWM
\n
"
);
return
ret
;
...
...
@@ -303,14 +315,16 @@ static int pwm_fan_suspend(struct device *dev)
static
int
pwm_fan_resume
(
struct
device
*
dev
)
{
struct
pwm_fan_ctx
*
ctx
=
dev_get_drvdata
(
dev
);
struct
pwm_args
pargs
;
unsigned
long
duty
;
int
ret
;
if
(
ctx
->
pwm_value
==
0
)
return
0
;
duty
=
DIV_ROUND_UP
(
ctx
->
pwm_value
*
(
ctx
->
pwm
->
period
-
1
),
MAX_PWM
);
ret
=
pwm_config
(
ctx
->
pwm
,
duty
,
ctx
->
pwm
->
period
);
pwm_get_args
(
ctx
->
pwm
,
&
pargs
);
duty
=
DIV_ROUND_UP
(
ctx
->
pwm_value
*
(
pargs
.
period
-
1
),
MAX_PWM
);
ret
=
pwm_config
(
ctx
->
pwm
,
duty
,
pargs
.
period
);
if
(
ret
)
return
ret
;
return
pwm_enable
(
ctx
->
pwm
);
...
...
drivers/input/misc/max77693-haptic.c
View file @
18c58878
...
...
@@ -70,10 +70,13 @@ struct max77693_haptic {
static
int
max77693_haptic_set_duty_cycle
(
struct
max77693_haptic
*
haptic
)
{
int
delta
=
(
haptic
->
pwm_dev
->
period
+
haptic
->
pwm_duty
)
/
2
;
struct
pwm_args
pargs
;
int
delta
;
int
error
;
error
=
pwm_config
(
haptic
->
pwm_dev
,
delta
,
haptic
->
pwm_dev
->
period
);
pwm_get_args
(
haptic
->
pwm_dev
,
&
pargs
);
delta
=
(
pargs
.
period
+
haptic
->
pwm_duty
)
/
2
;
error
=
pwm_config
(
haptic
->
pwm_dev
,
delta
,
pargs
.
period
);
if
(
error
)
{
dev_err
(
haptic
->
dev
,
"failed to configure pwm: %d
\n
"
,
error
);
return
error
;
...
...
@@ -234,6 +237,7 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
struct
ff_effect
*
effect
)
{
struct
max77693_haptic
*
haptic
=
input_get_drvdata
(
dev
);
struct
pwm_args
pargs
;
u64
period_mag_multi
;
haptic
->
magnitude
=
effect
->
u
.
rumble
.
strong_magnitude
;
...
...
@@ -245,7 +249,8 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
* The formula to convert magnitude to pwm_duty as follows:
* - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF)
*/
period_mag_multi
=
(
u64
)
haptic
->
pwm_dev
->
period
*
haptic
->
magnitude
;
pwm_get_args
(
haptic
->
pwm_dev
,
&
pargs
);
period_mag_multi
=
(
u64
)
pargs
.
period
*
haptic
->
magnitude
;
haptic
->
pwm_duty
=
(
unsigned
int
)(
period_mag_multi
>>
MAX_MAGNITUDE_SHIFT
);
...
...
@@ -329,6 +334,12 @@ static int max77693_haptic_probe(struct platform_device *pdev)
return
PTR_ERR
(
haptic
->
pwm_dev
);
}
/*
* FIXME: pwm_apply_args() should be removed when switching to the
* atomic PWM API.
*/
pwm_apply_args
(
haptic
->
pwm_dev
);
haptic
->
motor_reg
=
devm_regulator_get
(
&
pdev
->
dev
,
"haptic"
);
if
(
IS_ERR
(
haptic
->
motor_reg
))
{
dev_err
(
&
pdev
->
dev
,
"failed to get regulator
\n
"
);
...
...
drivers/input/misc/max8997_haptic.c
View file @
18c58878
...
...
@@ -304,6 +304,12 @@ static int max8997_haptic_probe(struct platform_device *pdev)
error
);
goto
err_free_mem
;
}
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args
(
chip
->
pwm
);
break
;
default:
...
...
drivers/input/misc/pwm-beeper.c
View file @
18c58878
...
...
@@ -87,6 +87,12 @@ static int pwm_beeper_probe(struct platform_device *pdev)
goto
err_free
;
}
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args
(
beeper
->
pwm
);
beeper
->
input
=
input_allocate_device
();
if
(
!
beeper
->
input
)
{
dev_err
(
&
pdev
->
dev
,
"Failed to allocate input device
\n
"
);
...
...
drivers/leds/leds-pwm.c
View file @
18c58878
...
...
@@ -91,6 +91,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
struct
led_pwm
*
led
,
struct
device_node
*
child
)
{
struct
led_pwm_data
*
led_data
=
&
priv
->
leds
[
priv
->
num_leds
];
struct
pwm_args
pargs
;
int
ret
;
led_data
->
active_low
=
led
->
active_low
;
...
...
@@ -117,7 +118,15 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
else
led_data
->
cdev
.
brightness_set_blocking
=
led_pwm_set_blocking
;
led_data
->
period
=
pwm_get_period
(
led_data
->
pwm
);
/*
* FIXME: pwm_apply_args() should be removed when switching to the
* atomic PWM API.
*/
pwm_apply_args
(
led_data
->
pwm
);
pwm_get_args
(
led_data
->
pwm
,
&
pargs
);
led_data
->
period
=
pargs
.
period
;
if
(
!
led_data
->
period
&&
(
led
->
pwm_period_ns
>
0
))
led_data
->
period
=
led
->
pwm_period_ns
;
...
...
drivers/pwm/core.c
View file @
18c58878
...
...
@@ -227,6 +227,19 @@ void *pwm_get_chip_data(struct pwm_device *pwm)
}
EXPORT_SYMBOL_GPL
(
pwm_get_chip_data
);
static
bool
pwm_ops_check
(
const
struct
pwm_ops
*
ops
)
{
/* driver supports legacy, non-atomic operation */
if
(
ops
->
config
&&
ops
->
enable
&&
ops
->
disable
)
return
true
;
/* driver supports atomic operation */
if
(
ops
->
apply
)
return
true
;
return
false
;
}
/**
* pwmchip_add_with_polarity() - register a new PWM chip
* @chip: the PWM chip to add
...
...
@@ -245,8 +258,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
unsigned
int
i
;
int
ret
;
if
(
!
chip
||
!
chip
->
dev
||
!
chip
->
ops
||
!
chip
->
ops
->
config
||
!
chip
->
ops
->
enable
||
!
chip
->
ops
->
disable
||
!
chip
->
npwm
)
if
(
!
chip
||
!
chip
->
dev
||
!
chip
->
ops
||
!
chip
->
npwm
)
return
-
EINVAL
;
if
(
!
pwm_ops_check
(
chip
->
ops
))
return
-
EINVAL
;
mutex_lock
(
&
pwm_lock
);
...
...
@@ -269,8 +284,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
pwm
->
chip
=
chip
;
pwm
->
pwm
=
chip
->
base
+
i
;
pwm
->
hwpwm
=
i
;
pwm
->
polarity
=
polarity
;
mutex_init
(
&
pwm
->
lock
);
pwm
->
state
.
polarity
=
polarity
;
if
(
chip
->
ops
->
get_state
)
chip
->
ops
->
get_state
(
chip
,
pwm
,
&
pwm
->
state
);
radix_tree_insert
(
&
pwm_tree
,
pwm
->
pwm
,
pwm
);
}
...
...
@@ -430,107 +447,138 @@ void pwm_free(struct pwm_device *pwm)
EXPORT_SYMBOL_GPL
(
pwm_free
);
/**
* pwm_
config() - change a PWM device configuration
* pwm_
apply_state() - atomically apply a new state to a PWM device
* @pwm: PWM device
* @duty_ns: "on" time (in nanoseconds)
* @period_ns: duration (in nanoseconds) of one cycle
*
* Returns: 0 on success or a negative error code on failure.
* @state: new state to apply. This can be adjusted by the PWM driver
* if the requested config is not achievable, for example,
* ->duty_cycle and ->period might be approximated.
*/
int
pwm_
config
(
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
int
pwm_
apply_state
(
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
int
err
;
if
(
!
pwm
||
duty_ns
<
0
||
period_ns
<=
0
||
duty_ns
>
period_ns
)
if
(
!
pwm
)
return
-
EINVAL
;
err
=
pwm
->
chip
->
ops
->
config
(
pwm
->
chip
,
pwm
,
duty_ns
,
period_ns
);
if
(
!
memcmp
(
state
,
&
pwm
->
state
,
sizeof
(
*
state
)))
return
0
;
if
(
pwm
->
chip
->
ops
->
apply
)
{
err
=
pwm
->
chip
->
ops
->
apply
(
pwm
->
chip
,
pwm
,
state
);
if
(
err
)
return
err
;
pwm
->
duty_cycle
=
duty_ns
;
pwm
->
period
=
period_ns
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
pwm_config
);
pwm
->
state
=
*
state
;
}
else
{
/*
* FIXME: restore the initial state in case of error.
*/
if
(
state
->
polarity
!=
pwm
->
state
.
polarity
)
{
if
(
!
pwm
->
chip
->
ops
->
set_polarity
)
return
-
ENOTSUPP
;
/**
* pwm_set_polarity() - configure the polarity of a PWM signal
* @pwm: PWM device
* @polarity: new polarity of the PWM signal
*
* Note that the polarity cannot be configured while the PWM device is
* enabled.
*
* Returns: 0 on success or a negative error code on failure.
/*
* Changing the polarity of a running PWM is
* only allowed when the PWM driver implements
* ->apply().
*/
int
pwm_set_polarity
(
struct
pwm_device
*
pwm
,
enum
pwm_polarity
polarity
)
{
int
err
;
if
(
pwm
->
state
.
enabled
)
{
pwm
->
chip
->
ops
->
disable
(
pwm
->
chip
,
pwm
);
pwm
->
state
.
enabled
=
false
;
}
if
(
!
pwm
||
!
pwm
->
chip
->
ops
)
return
-
EINVAL
;
err
=
pwm
->
chip
->
ops
->
set_polarity
(
pwm
->
chip
,
pwm
,
state
->
polarity
);
if
(
err
)
return
err
;
if
(
!
pwm
->
chip
->
ops
->
set_polarity
)
return
-
ENOSYS
;
pwm
->
state
.
polarity
=
state
->
polarity
;
}
mutex_lock
(
&
pwm
->
lock
);
if
(
state
->
period
!=
pwm
->
state
.
period
||
state
->
duty_cycle
!=
pwm
->
state
.
duty_cycle
)
{
err
=
pwm
->
chip
->
ops
->
config
(
pwm
->
chip
,
pwm
,
state
->
duty_cycle
,
state
->
period
);
if
(
err
)
return
err
;
if
(
pwm_is_enabled
(
pwm
))
{
err
=
-
EBUSY
;
goto
unlock
;
pwm
->
state
.
duty_cycle
=
state
->
duty_cycle
;
pwm
->
state
.
period
=
state
->
period
;
}
err
=
pwm
->
chip
->
ops
->
set_polarity
(
pwm
->
chip
,
pwm
,
polarity
);
if
(
state
->
enabled
!=
pwm
->
state
.
enabled
)
{
if
(
state
->
enabled
)
{
err
=
pwm
->
chip
->
ops
->
enable
(
pwm
->
chip
,
pwm
);
if
(
err
)
goto
unlock
;
return
err
;
}
else
{
pwm
->
chip
->
ops
->
disable
(
pwm
->
chip
,
pwm
);
}
pwm
->
polarity
=
polarity
;
pwm
->
state
.
enabled
=
state
->
enabled
;
}
}
unlock:
mutex_unlock
(
&
pwm
->
lock
);
return
err
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
pwm_
set_polarity
);
EXPORT_SYMBOL_GPL
(
pwm_
apply_state
);
/**
* pwm_
enable() - start a PWM output toggling
* pwm_
adjust_config() - adjust the current PWM config to the PWM arguments
* @pwm: PWM device
*
* Returns: 0 on success or a negative error code on failure.
* This function will adjust the PWM config to the PWM arguments provided
* by the DT or PWM lookup table. This is particularly useful to adapt
* the bootloader config to the Linux one.
*/
int
pwm_
enable
(
struct
pwm_device
*
pwm
)
int
pwm_
adjust_config
(
struct
pwm_device
*
pwm
)
{
int
err
=
0
;
struct
pwm_state
state
;
struct
pwm_args
pargs
;
if
(
!
pwm
)
return
-
EINVAL
;
pwm_get_args
(
pwm
,
&
pargs
);
pwm_get_state
(
pwm
,
&
state
)
;
mutex_lock
(
&
pwm
->
lock
);
/*
* If the current period is zero it means that either the PWM driver
* does not support initial state retrieval or the PWM has not yet
* been configured.
*
* In either case, we setup the new period and polarity, and assign a
* duty cycle of 0.
*/
if
(
!
state
.
period
)
{
state
.
duty_cycle
=
0
;
state
.
period
=
pargs
.
period
;
state
.
polarity
=
pargs
.
polarity
;
if
(
!
test_and_set_bit
(
PWMF_ENABLED
,
&
pwm
->
flags
))
{
err
=
pwm
->
chip
->
ops
->
enable
(
pwm
->
chip
,
pwm
);
if
(
err
)
clear_bit
(
PWMF_ENABLED
,
&
pwm
->
flags
);
return
pwm_apply_state
(
pwm
,
&
state
);
}
mutex_unlock
(
&
pwm
->
lock
);
/*
* Adjust the PWM duty cycle/period based on the period value provided
* in PWM args.
*/
if
(
pargs
.
period
!=
state
.
period
)
{
u64
dutycycle
=
(
u64
)
state
.
duty_cycle
*
pargs
.
period
;
return
err
;
}
EXPORT_SYMBOL_GPL
(
pwm_enable
);
do_div
(
dutycycle
,
state
.
period
);
state
.
duty_cycle
=
dutycycle
;
state
.
period
=
pargs
.
period
;
}
/**
* pwm_disable() - stop a PWM output toggling
* @pwm: PWM device
/*
* If the polarity changed, we should also change the duty cycle.
*/
void
pwm_disable
(
struct
pwm_device
*
pwm
)
{
if
(
pwm
&&
test_and_clear_bit
(
PWMF_ENABLED
,
&
pwm
->
flags
))
pwm
->
chip
->
ops
->
disable
(
pwm
->
chip
,
pwm
);
if
(
pargs
.
polarity
!=
state
.
polarity
)
{
state
.
polarity
=
pargs
.
polarity
;
state
.
duty_cycle
=
state
.
period
-
state
.
duty_cycle
;
}
return
pwm_apply_state
(
pwm
,
&
state
);
}
EXPORT_SYMBOL_GPL
(
pwm_
disable
);
EXPORT_SYMBOL_GPL
(
pwm_
adjust_config
);
static
struct
pwm_chip
*
of_node_to_pwmchip
(
struct
device_node
*
np
)
{
...
...
@@ -621,13 +669,6 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
pwm
->
label
=
con_id
;
/*
* FIXME: This should be removed once all PWM users properly make use
* of struct pwm_args to initialize the PWM device. As long as this is
* here, the PWM state and hardware state can get out of sync.
*/
pwm_apply_args
(
pwm
);
put:
of_node_put
(
args
.
np
);
...
...
@@ -762,13 +803,6 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
pwm
->
args
.
period
=
chosen
->
period
;
pwm
->
args
.
polarity
=
chosen
->
polarity
;
/*
* FIXME: This should be removed once all PWM users properly make use
* of struct pwm_args to initialize the PWM device. As long as this is
* here, the PWM state and hardware state can get out of sync.
*/
pwm_apply_args
(
pwm
);
out:
mutex_unlock
(
&
pwm_lookup_lock
);
return
pwm
;
...
...
@@ -915,15 +949,23 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
for
(
i
=
0
;
i
<
chip
->
npwm
;
i
++
)
{
struct
pwm_device
*
pwm
=
&
chip
->
pwms
[
i
];
struct
pwm_state
state
;
pwm_get_state
(
pwm
,
&
state
);
seq_printf
(
s
,
" pwm-%-3d (%-20.20s):"
,
i
,
pwm
->
label
);
if
(
test_bit
(
PWMF_REQUESTED
,
&
pwm
->
flags
))
seq_puts
(
s
,
" requested"
);
if
(
pwm_is_enabled
(
pwm
)
)
if
(
state
.
enabled
)
seq_puts
(
s
,
" enabled"
);
seq_printf
(
s
,
" period: %u ns"
,
state
.
period
);
seq_printf
(
s
,
" duty: %u ns"
,
state
.
duty_cycle
);
seq_printf
(
s
,
" polarity: %s"
,
state
.
polarity
?
"inverse"
:
"normal"
);
seq_puts
(
s
,
"
\n
"
);
}
}
...
...
drivers/pwm/pwm-crc.c
View file @
18c58878
...
...
@@ -75,7 +75,7 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm,
return
-
EINVAL
;
}
if
(
pwm
->
period
!=
period_ns
)
{
if
(
pwm
_get_period
(
pwm
)
!=
period_ns
)
{
int
clk_div
;
/* changing the clk divisor, need to disable fisrt */
...
...
drivers/pwm/pwm-lpc18xx-sct.c
View file @
18c58878
...
...
@@ -249,7 +249,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
LPC18XX_PWM_EVSTATEMSK
(
lpc18xx_data
->
duty_event
),
LPC18XX_PWM_EVSTATEMSK_ALL
);
if
(
pwm
->
polarity
==
PWM_POLARITY_NORMAL
)
{
if
(
pwm
_get_polarity
(
pwm
)
==
PWM_POLARITY_NORMAL
)
{
set_event
=
lpc18xx_pwm
->
period_event
;
clear_event
=
lpc18xx_data
->
duty_event
;
res_action
=
LPC18XX_PWM_RES_SET
;
...
...
drivers/pwm/pwm-omap-dmtimer.c
View file @
18c58878
...
...
@@ -192,7 +192,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
load_value
,
load_value
,
match_value
,
match_value
);
omap
->
pdata
->
set_pwm
(
omap
->
dm_timer
,
pwm
->
polarity
==
PWM_POLARITY_INVERSED
,
pwm
_get_polarity
(
pwm
)
==
PWM_POLARITY_INVERSED
,
true
,
PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE
);
...
...
drivers/pwm/pwm-rcar.c
View file @
18c58878
...
...
@@ -157,7 +157,7 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return
div
;
/* Let the core driver set pwm->period if disabled and duty_ns == 0 */
if
(
!
test_bit
(
PWMF_ENABLED
,
&
pwm
->
flags
)
&&
!
duty_ns
)
if
(
!
pwm_is_enabled
(
pwm
)
&&
!
duty_ns
)
return
0
;
rcar_pwm_update
(
rp
,
RCAR_PWMCR_SYNC
,
RCAR_PWMCR_SYNC
,
RCAR_PWMCR
);
...
...
drivers/pwm/pwm-sun4i.c
View file @
18c58878
...
...
@@ -354,7 +354,8 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
val
=
sun4i_pwm_readl
(
pwm
,
PWM_CTRL_REG
);
for
(
i
=
0
;
i
<
pwm
->
chip
.
npwm
;
i
++
)
if
(
!
(
val
&
BIT_CH
(
PWM_ACT_STATE
,
i
)))
pwm
->
chip
.
pwms
[
i
].
polarity
=
PWM_POLARITY_INVERSED
;
pwm_set_polarity
(
&
pwm
->
chip
.
pwms
[
i
],
PWM_POLARITY_INVERSED
);
clk_disable_unprepare
(
pwm
->
clk
);
return
0
;
...
...
drivers/pwm/sysfs.c
View file @
18c58878
...
...
@@ -26,6 +26,7 @@
struct
pwm_export
{
struct
device
child
;
struct
pwm_device
*
pwm
;
struct
mutex
lock
;
};
static
struct
pwm_export
*
child_to_pwm_export
(
struct
device
*
child
)
...
...
@@ -45,15 +46,20 @@ static ssize_t period_show(struct device *child,
char
*
buf
)
{
const
struct
pwm_device
*
pwm
=
child_to_pwm_device
(
child
);
struct
pwm_state
state
;
return
sprintf
(
buf
,
"%u
\n
"
,
pwm_get_period
(
pwm
));
pwm_get_state
(
pwm
,
&
state
);
return
sprintf
(
buf
,
"%u
\n
"
,
state
.
period
);
}
static
ssize_t
period_store
(
struct
device
*
child
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
size
)
{
struct
pwm_device
*
pwm
=
child_to_pwm_device
(
child
);
struct
pwm_export
*
export
=
child_to_pwm_export
(
child
);
struct
pwm_device
*
pwm
=
export
->
pwm
;
struct
pwm_state
state
;
unsigned
int
val
;
int
ret
;
...
...
@@ -61,7 +67,11 @@ static ssize_t period_store(struct device *child,
if
(
ret
)
return
ret
;
ret
=
pwm_config
(
pwm
,
pwm_get_duty_cycle
(
pwm
),
val
);
mutex_lock
(
&
export
->
lock
);
pwm_get_state
(
pwm
,
&
state
);
state
.
period
=
val
;
ret
=
pwm_apply_state
(
pwm
,
&
state
);
mutex_unlock
(
&
export
->
lock
);
return
ret
?
:
size
;
}
...
...
@@ -71,15 +81,20 @@ static ssize_t duty_cycle_show(struct device *child,
char
*
buf
)
{
const
struct
pwm_device
*
pwm
=
child_to_pwm_device
(
child
);
struct
pwm_state
state
;
pwm_get_state
(
pwm
,
&
state
);
return
sprintf
(
buf
,
"%u
\n
"
,
pwm_get_duty_cycle
(
pwm
)
);
return
sprintf
(
buf
,
"%u
\n
"
,
state
.
duty_cycle
);
}
static
ssize_t
duty_cycle_store
(
struct
device
*
child
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
size
)
{
struct
pwm_device
*
pwm
=
child_to_pwm_device
(
child
);
struct
pwm_export
*
export
=
child_to_pwm_export
(
child
);
struct
pwm_device
*
pwm
=
export
->
pwm
;
struct
pwm_state
state
;
unsigned
int
val
;
int
ret
;
...
...
@@ -87,7 +102,11 @@ static ssize_t duty_cycle_store(struct device *child,
if
(
ret
)
return
ret
;
ret
=
pwm_config
(
pwm
,
val
,
pwm_get_period
(
pwm
));
mutex_lock
(
&
export
->
lock
);
pwm_get_state
(
pwm
,
&
state
);
state
.
duty_cycle
=
val
;
ret
=
pwm_apply_state
(
pwm
,
&
state
);
mutex_unlock
(
&
export
->
lock
);
return
ret
?
:
size
;
}
...
...
@@ -97,33 +116,46 @@ static ssize_t enable_show(struct device *child,
char
*
buf
)
{
const
struct
pwm_device
*
pwm
=
child_to_pwm_device
(
child
);
struct
pwm_state
state
;
pwm_get_state
(
pwm
,
&
state
);
return
sprintf
(
buf
,
"%d
\n
"
,
pwm_is_enabled
(
pwm
)
);
return
sprintf
(
buf
,
"%d
\n
"
,
state
.
enabled
);
}
static
ssize_t
enable_store
(
struct
device
*
child
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
size
)
{
struct
pwm_device
*
pwm
=
child_to_pwm_device
(
child
);
struct
pwm_export
*
export
=
child_to_pwm_export
(
child
);
struct
pwm_device
*
pwm
=
export
->
pwm
;
struct
pwm_state
state
;
int
val
,
ret
;
ret
=
kstrtoint
(
buf
,
0
,
&
val
);
if
(
ret
)
return
ret
;
mutex_lock
(
&
export
->
lock
);
pwm_get_state
(
pwm
,
&
state
);
switch
(
val
)
{
case
0
:
pwm_disable
(
pwm
)
;
state
.
enabled
=
false
;
break
;
case
1
:
ret
=
pwm_enable
(
pwm
)
;
state
.
enabled
=
true
;
break
;
default:
ret
=
-
EINVAL
;
brea
k
;
goto
unloc
k
;
}
pwm_apply_state
(
pwm
,
&
state
);
unlock:
mutex_unlock
(
&
export
->
lock
);
return
ret
?
:
size
;
}
...
...
@@ -133,8 +165,11 @@ static ssize_t polarity_show(struct device *child,
{
const
struct
pwm_device
*
pwm
=
child_to_pwm_device
(
child
);
const
char
*
polarity
=
"unknown"
;
struct
pwm_state
state
;
pwm_get_state
(
pwm
,
&
state
);
switch
(
pwm_get_polarity
(
pwm
)
)
{
switch
(
state
.
polarity
)
{
case
PWM_POLARITY_NORMAL
:
polarity
=
"normal"
;
break
;
...
...
@@ -151,8 +186,10 @@ static ssize_t polarity_store(struct device *child,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
size
)
{
struct
pwm_device
*
pwm
=
child_to_pwm_device
(
child
);
struct
pwm_export
*
export
=
child_to_pwm_export
(
child
);
struct
pwm_device
*
pwm
=
export
->
pwm
;
enum
pwm_polarity
polarity
;
struct
pwm_state
state
;
int
ret
;
if
(
sysfs_streq
(
buf
,
"normal"
))
...
...
@@ -162,7 +199,11 @@ static ssize_t polarity_store(struct device *child,
else
return
-
EINVAL
;
ret
=
pwm_set_polarity
(
pwm
,
polarity
);
mutex_lock
(
&
export
->
lock
);
pwm_get_state
(
pwm
,
&
state
);
state
.
polarity
=
polarity
;
ret
=
pwm_apply_state
(
pwm
,
&
state
);
mutex_unlock
(
&
export
->
lock
);
return
ret
?
:
size
;
}
...
...
@@ -203,6 +244,7 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
}
export
->
pwm
=
pwm
;
mutex_init
(
&
export
->
lock
);
export
->
child
.
release
=
pwm_export_release
;
export
->
child
.
parent
=
parent
;
...
...
drivers/video/backlight/lm3630a_bl.c
View file @
18c58878
...
...
@@ -162,7 +162,7 @@ static int lm3630a_intr_config(struct lm3630a_chip *pchip)
static
void
lm3630a_pwm_ctrl
(
struct
lm3630a_chip
*
pchip
,
int
br
,
int
br_max
)
{
unsigned
int
period
=
p
wm_get_period
(
pchip
->
pwmd
)
;
unsigned
int
period
=
p
chip
->
pdata
->
pwm_period
;
unsigned
int
duty
=
br
*
period
/
br_max
;
pwm_config
(
pchip
->
pwmd
,
duty
,
period
);
...
...
@@ -424,8 +424,13 @@ static int lm3630a_probe(struct i2c_client *client,
dev_err
(
&
client
->
dev
,
"fail : get pwm device
\n
"
);
return
PTR_ERR
(
pchip
->
pwmd
);
}
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args
(
pchip
->
pwmd
);
}
pchip
->
pwmd
->
period
=
pdata
->
pwm_period
;
/* interrupt enable : irq 0 is not allowed */
pchip
->
irq
=
client
->
irq
;
...
...
drivers/video/backlight/lp855x_bl.c
View file @
18c58878
...
...
@@ -246,6 +246,12 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
return
;
lp
->
pwm
=
pwm
;
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args
(
pwm
);
}
pwm_config
(
lp
->
pwm
,
duty
,
period
);
...
...
drivers/video/backlight/lp8788_bl.c
View file @
18c58878
...
...
@@ -145,6 +145,12 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
}
bl
->
pwm
=
pwm
;
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args
(
pwm
);
}
pwm_config
(
bl
->
pwm
,
duty
,
period
);
...
...
drivers/video/backlight/pwm_bl.c
View file @
18c58878
...
...
@@ -201,6 +201,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
struct
device_node
*
node
=
pdev
->
dev
.
of_node
;
struct
pwm_bl_data
*
pb
;
int
initial_blank
=
FB_BLANK_UNBLANK
;
struct
pwm_args
pargs
;
int
ret
;
if
(
!
data
)
{
...
...
@@ -306,17 +307,22 @@ static int pwm_backlight_probe(struct platform_device *pdev)
dev_dbg
(
&
pdev
->
dev
,
"got pwm for backlight
\n
"
);
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args
(
pb
->
pwm
);
/*
* 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 it has not already been set
* via the PWM lookup table.
*/
pb
->
period
=
pwm_get_period
(
pb
->
pwm
);
if
(
!
pb
->
period
&&
(
data
->
pwm_period_ns
>
0
))
{
pwm_get_args
(
pb
->
pwm
,
&
pargs
);
pb
->
period
=
pargs
.
period
;
if
(
!
pb
->
period
&&
(
data
->
pwm_period_ns
>
0
))
pb
->
period
=
data
->
pwm_period_ns
;
pwm_set_period
(
pb
->
pwm
,
data
->
pwm_period_ns
);
}
pb
->
lth_brightness
=
data
->
lth_brightness
*
(
pb
->
period
/
pb
->
scale
);
...
...
drivers/video/fbdev/ssd1307fb.c
View file @
18c58878
...
...
@@ -286,6 +286,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
{
int
ret
;
u32
precharge
,
dclk
,
com_invdir
,
compins
;
struct
pwm_args
pargs
;
if
(
par
->
device_info
->
need_pwm
)
{
par
->
pwm
=
pwm_get
(
&
par
->
client
->
dev
,
NULL
);
...
...
@@ -294,7 +295,15 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
return
PTR_ERR
(
par
->
pwm
);
}
par
->
pwm_period
=
pwm_get_period
(
par
->
pwm
);
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args
(
par
->
pwm
);
pwm_get_args
(
par
->
pwm
,
&
pargs
);
par
->
pwm_period
=
pargs
.
period
;
/* Enable the PWM */
pwm_config
(
par
->
pwm
,
par
->
pwm_period
/
2
,
par
->
pwm_period
);
pwm_enable
(
par
->
pwm
);
...
...
include/linux/pwm.h
View file @
18c58878
...
...
@@ -5,59 +5,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
struct
pwm_device
;
struct
seq_file
;
#if IS_ENABLED(CONFIG_PWM)
/*
* pwm_request - request a PWM device
*/
struct
pwm_device
*
pwm_request
(
int
pwm_id
,
const
char
*
label
);
/*
* pwm_free - free a PWM device
*/
void
pwm_free
(
struct
pwm_device
*
pwm
);
/*
* pwm_config - change a PWM device configuration
*/
int
pwm_config
(
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
);
/*
* pwm_enable - start a PWM output toggling
*/
int
pwm_enable
(
struct
pwm_device
*
pwm
);
/*
* pwm_disable - stop a PWM output toggling
*/
void
pwm_disable
(
struct
pwm_device
*
pwm
);
#else
static
inline
struct
pwm_device
*
pwm_request
(
int
pwm_id
,
const
char
*
label
)
{
return
ERR_PTR
(
-
ENODEV
);
}
static
inline
void
pwm_free
(
struct
pwm_device
*
pwm
)
{
}
static
inline
int
pwm_config
(
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
{
return
-
EINVAL
;
}
static
inline
int
pwm_enable
(
struct
pwm_device
*
pwm
)
{
return
-
EINVAL
;
}
static
inline
void
pwm_disable
(
struct
pwm_device
*
pwm
)
{
}
#endif
struct
pwm_chip
;
/**
...
...
@@ -94,8 +42,21 @@ struct pwm_args {
enum
{
PWMF_REQUESTED
=
1
<<
0
,
PWMF_ENABLED
=
1
<<
1
,
PWMF_EXPORTED
=
1
<<
2
,
PWMF_EXPORTED
=
1
<<
1
,
};
/*
* struct pwm_state - state of a PWM channel
* @period: PWM period (in nanoseconds)
* @duty_cycle: PWM duty cycle (in nanoseconds)
* @polarity: PWM polarity
* @enabled: PWM enabled status
*/
struct
pwm_state
{
unsigned
int
period
;
unsigned
int
duty_cycle
;
enum
pwm_polarity
polarity
;
bool
enabled
;
};
/**
...
...
@@ -106,11 +67,8 @@ enum {
* @pwm: global index of the PWM device
* @chip: PWM chip providing this PWM device
* @chip_data: chip-private data associated with the PWM device
* @lock: used to serialize accesses to the PWM device where necessary
* @period: period of the PWM signal (in nanoseconds)
* @duty_cycle: duty cycle of the PWM signal (in nanoseconds)
* @polarity: polarity of the PWM signal
* @args: PWM arguments
* @state: curent PWM channel state
*/
struct
pwm_device
{
const
char
*
label
;
...
...
@@ -119,50 +77,68 @@ struct pwm_device {
unsigned
int
pwm
;
struct
pwm_chip
*
chip
;
void
*
chip_data
;
struct
mutex
lock
;
unsigned
int
period
;
unsigned
int
duty_cycle
;
enum
pwm_polarity
polarity
;
struct
pwm_args
args
;
struct
pwm_state
state
;
};
/**
* pwm_get_state() - retrieve the current PWM state
* @pwm: PWM device
* @state: state to fill with the current PWM state
*/
static
inline
void
pwm_get_state
(
const
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
*
state
=
pwm
->
state
;
}
static
inline
bool
pwm_is_enabled
(
const
struct
pwm_device
*
pwm
)
{
return
test_bit
(
PWMF_ENABLED
,
&
pwm
->
flags
);
struct
pwm_state
state
;
pwm_get_state
(
pwm
,
&
state
);
return
state
.
enabled
;
}
static
inline
void
pwm_set_period
(
struct
pwm_device
*
pwm
,
unsigned
int
period
)
{
if
(
pwm
)
pwm
->
period
=
period
;
pwm
->
state
.
period
=
period
;
}
static
inline
unsigned
int
pwm_get_period
(
const
struct
pwm_device
*
pwm
)
{
return
pwm
?
pwm
->
period
:
0
;
struct
pwm_state
state
;
pwm_get_state
(
pwm
,
&
state
);
return
state
.
period
;
}
static
inline
void
pwm_set_duty_cycle
(
struct
pwm_device
*
pwm
,
unsigned
int
duty
)
{
if
(
pwm
)
pwm
->
duty_cycle
=
duty
;
pwm
->
state
.
duty_cycle
=
duty
;
}
static
inline
unsigned
int
pwm_get_duty_cycle
(
const
struct
pwm_device
*
pwm
)
{
return
pwm
?
pwm
->
duty_cycle
:
0
;
}
struct
pwm_state
state
;
/*
* pwm_set_polarity - configure the polarity of a PWM signal
*/
int
pwm_set_polarity
(
struct
pwm_device
*
pwm
,
enum
pwm_polarity
polarity
);
pwm_get_state
(
pwm
,
&
state
);
return
state
.
duty_cycle
;
}
static
inline
enum
pwm_polarity
pwm_get_polarity
(
const
struct
pwm_device
*
pwm
)
{
return
pwm
?
pwm
->
polarity
:
PWM_POLARITY_NORMAL
;
struct
pwm_state
state
;
pwm_get_state
(
pwm
,
&
state
);
return
state
.
polarity
;
}
static
inline
void
pwm_get_args
(
const
struct
pwm_device
*
pwm
,
...
...
@@ -171,12 +147,6 @@ static inline void pwm_get_args(const struct pwm_device *pwm,
*
args
=
pwm
->
args
;
}
static
inline
void
pwm_apply_args
(
struct
pwm_device
*
pwm
)
{
pwm_set_period
(
pwm
,
pwm
->
args
.
period
);
pwm_set_polarity
(
pwm
,
pwm
->
args
.
polarity
);
}
/**
* struct pwm_ops - PWM controller operations
* @request: optional hook for requesting a PWM
...
...
@@ -185,6 +155,13 @@ static inline void pwm_apply_args(struct pwm_device *pwm)
* @set_polarity: configure the polarity of this PWM
* @enable: enable PWM output toggling
* @disable: disable PWM output toggling
* @apply: atomically apply a new PWM config. The state argument
* should be adjusted with the real hardware config (if the
* approximate the period or duty_cycle value, state should
* reflect it)
* @get_state: get the current PWM state. This function is only
* called once per PWM device when the PWM chip is
* registered.
* @dbg_show: optional routine to show contents in debugfs
* @owner: helps prevent removal of modules exporting active PWMs
*/
...
...
@@ -197,6 +174,10 @@ struct pwm_ops {
enum
pwm_polarity
polarity
);
int
(
*
enable
)(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
);
void
(
*
disable
)(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
);
int
(
*
apply
)(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
);
void
(
*
get_state
)(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
);
#ifdef CONFIG_DEBUG_FS
void
(
*
dbg_show
)(
struct
pwm_chip
*
chip
,
struct
seq_file
*
s
);
#endif
...
...
@@ -232,6 +213,115 @@ struct pwm_chip {
};
#if IS_ENABLED(CONFIG_PWM)
/* PWM user APIs */
struct
pwm_device
*
pwm_request
(
int
pwm_id
,
const
char
*
label
);
void
pwm_free
(
struct
pwm_device
*
pwm
);
int
pwm_apply_state
(
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
);
int
pwm_adjust_config
(
struct
pwm_device
*
pwm
);
/**
* pwm_config() - change a PWM device configuration
* @pwm: PWM device
* @duty_ns: "on" time (in nanoseconds)
* @period_ns: duration (in nanoseconds) of one cycle
*
* Returns: 0 on success or a negative error code on failure.
*/
static
inline
int
pwm_config
(
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
{
struct
pwm_state
state
;
if
(
!
pwm
)
return
-
EINVAL
;
pwm_get_state
(
pwm
,
&
state
);
if
(
state
.
duty_cycle
==
duty_ns
&&
state
.
period
==
period_ns
)
return
0
;
state
.
duty_cycle
=
duty_ns
;
state
.
period
=
period_ns
;
return
pwm_apply_state
(
pwm
,
&
state
);
}
/**
* pwm_set_polarity() - configure the polarity of a PWM signal
* @pwm: PWM device
* @polarity: new polarity of the PWM signal
*
* Note that the polarity cannot be configured while the PWM device is
* enabled.
*
* Returns: 0 on success or a negative error code on failure.
*/
static
inline
int
pwm_set_polarity
(
struct
pwm_device
*
pwm
,
enum
pwm_polarity
polarity
)
{
struct
pwm_state
state
;
if
(
!
pwm
)
return
-
EINVAL
;
pwm_get_state
(
pwm
,
&
state
);
if
(
state
.
polarity
==
polarity
)
return
0
;
/*
* Changing the polarity of a running PWM without adjusting the
* dutycycle/period value is a bit risky (can introduce glitches).
* Return -EBUSY in this case.
* Note that this is allowed when using pwm_apply_state() because
* the user specifies all the parameters.
*/
if
(
state
.
enabled
)
return
-
EBUSY
;
state
.
polarity
=
polarity
;
return
pwm_apply_state
(
pwm
,
&
state
);
}
/**
* pwm_enable() - start a PWM output toggling
* @pwm: PWM device
*
* Returns: 0 on success or a negative error code on failure.
*/
static
inline
int
pwm_enable
(
struct
pwm_device
*
pwm
)
{
struct
pwm_state
state
;
if
(
!
pwm
)
return
-
EINVAL
;
pwm_get_state
(
pwm
,
&
state
);
if
(
state
.
enabled
)
return
0
;
state
.
enabled
=
true
;
return
pwm_apply_state
(
pwm
,
&
state
);
}
/**
* pwm_disable() - stop a PWM output toggling
* @pwm: PWM device
*/
static
inline
void
pwm_disable
(
struct
pwm_device
*
pwm
)
{
struct
pwm_state
state
;
if
(
!
pwm
)
return
;
pwm_get_state
(
pwm
,
&
state
);
if
(
!
state
.
enabled
)
return
;
state
.
enabled
=
false
;
pwm_apply_state
(
pwm
,
&
state
);
}
/* PWM provider APIs */
int
pwm_set_chip_data
(
struct
pwm_device
*
pwm
,
void
*
data
);
void
*
pwm_get_chip_data
(
struct
pwm_device
*
pwm
);
...
...
@@ -257,6 +347,47 @@ void devm_pwm_put(struct device *dev, struct pwm_device *pwm);
bool
pwm_can_sleep
(
struct
pwm_device
*
pwm
);
#else
static
inline
struct
pwm_device
*
pwm_request
(
int
pwm_id
,
const
char
*
label
)
{
return
ERR_PTR
(
-
ENODEV
);
}
static
inline
void
pwm_free
(
struct
pwm_device
*
pwm
)
{
}
static
inline
int
pwm_apply_state
(
struct
pwm_device
*
pwm
,
const
struct
pwm_state
*
state
)
{
return
-
ENOTSUPP
;
}
static
inline
int
pwm_adjust_config
(
struct
pwm_device
*
pwm
)
{
return
-
ENOTSUPP
;
}
static
inline
int
pwm_config
(
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
{
return
-
EINVAL
;
}
static
inline
int
pwm_set_polarity
(
struct
pwm_device
*
pwm
,
enum
pwm_polarity
polarity
)
{
return
-
ENOTSUPP
;
}
static
inline
int
pwm_enable
(
struct
pwm_device
*
pwm
)
{
return
-
EINVAL
;
}
static
inline
void
pwm_disable
(
struct
pwm_device
*
pwm
)
{
}
static
inline
int
pwm_set_chip_data
(
struct
pwm_device
*
pwm
,
void
*
data
)
{
return
-
EINVAL
;
...
...
@@ -328,6 +459,34 @@ static inline bool pwm_can_sleep(struct pwm_device *pwm)
}
#endif
static
inline
void
pwm_apply_args
(
struct
pwm_device
*
pwm
)
{
/*
* PWM users calling pwm_apply_args() expect to have a fresh config
* where the polarity and period are set according to pwm_args info.
* The problem is, polarity can only be changed when the PWM is
* disabled.
*
* PWM drivers supporting hardware readout may declare the PWM device
* as enabled, and prevent polarity setting, which changes from the
* existing behavior, where all PWM devices are declared as disabled
* at startup (even if they are actually enabled), thus authorizing
* polarity setting.
*
* Instead of setting ->enabled to false, we call pwm_disable()
* before pwm_set_polarity() to ensure that everything is configured
* as expected, and the PWM is really disabled when the user request
* it.
*
* Note that PWM users requiring a smooth handover between the
* bootloader and the kernel (like critical regulators controlled by
* PWM devices) will have to switch to the atomic API and avoid calling
* pwm_apply_args().
*/
pwm_disable
(
pwm
);
pwm_set_polarity
(
pwm
,
pwm
->
args
.
polarity
);
}
struct
pwm_lookup
{
struct
list_head
list
;
const
char
*
provider
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment