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
53de7c26
Commit
53de7c26
authored
Jul 25, 2016
by
Thierry Reding
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-4.8/regulator' into for-next
parents
070d9a93
58fd822b
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
173 additions
and
49 deletions
+173
-49
Documentation/devicetree/bindings/regulator/pwm-regulator.txt
...mentation/devicetree/bindings/regulator/pwm-regulator.txt
+25
-1
drivers/regulator/pwm-regulator.c
drivers/regulator/pwm-regulator.c
+148
-48
No files found.
Documentation/devicetree/bindings/regulator/pwm-regulator.txt
View file @
53de7c26
...
...
@@ -34,20 +34,44 @@ Only required for Voltage Table Mode:
First cell is voltage in microvolts (uV)
Second cell is duty-cycle in percent (%)
Optional properties for Continuous mode:
- pwm-dutycycle-unit: Integer value encoding the duty cycle unit. If not
defined, <100> is assumed, meaning that
pwm-dutycycle-range contains values expressed in
percent.
- pwm-dutycycle-range: Should contain 2 entries. The first entry is encoding
the dutycycle for regulator-min-microvolt and the
second one the dutycycle for regulator-max-microvolt.
Duty cycle values are expressed in pwm-dutycycle-unit.
If not defined, <0 100> is assumed.
NB: To be clear, if voltage-table is provided, then the device will be used
in Voltage Table Mode. If no voltage-table is provided, then the device will
be used in Continuous Voltage Mode.
Optional properties:
--------------------
- enable-gpios: GPIO to use to enable/disable the regulator
Any property defined as part of the core regulator binding can also be used.
(See: ../regulator/regulator.txt)
Continuous Voltage Example:
Continuous Voltage
With Enable GPIO
Example:
pwm_regulator {
compatible = "pwm-regulator;
pwms = <&pwm1 0 8448 0>;
enable-gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>;
regulator-min-microvolt = <1016000>;
regulator-max-microvolt = <1114000>;
regulator-name = "vdd_logic";
/* unit == per-mille */
pwm-dutycycle-unit = <1000>;
/*
* Inverted PWM logic, and the duty cycle range is limited
* to 30%-70%.
*/
pwm-dutycycle-range <700 300>; /* */
};
Voltage Table Example:
...
...
drivers/regulator/pwm-regulator.c
View file @
53de7c26
...
...
@@ -20,6 +20,13 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pwm.h>
#include <linux/gpio/consumer.h>
struct
pwm_continuous_reg_data
{
unsigned
int
min_uV_dutycycle
;
unsigned
int
max_uV_dutycycle
;
unsigned
int
dutycycle_unit
;
};
struct
pwm_regulator_data
{
/* Shared */
...
...
@@ -28,6 +35,9 @@ struct pwm_regulator_data {
/* Voltage table */
struct
pwm_voltages
*
duty_cycle_table
;
/* Continuous mode info */
struct
pwm_continuous_reg_data
continuous
;
/* regulator descriptor */
struct
regulator_desc
desc
;
...
...
@@ -36,8 +46,8 @@ struct pwm_regulator_data {
int
state
;
/*
Continuous voltage
*/
int
volt_uV
;
/*
Enable GPIO
*/
struct
gpio_desc
*
enb_gpio
;
};
struct
pwm_voltages
{
...
...
@@ -48,10 +58,31 @@ struct pwm_voltages {
/**
* Voltage table call-backs
*/
static
void
pwm_regulator_init_state
(
struct
regulator_dev
*
rdev
)
{
struct
pwm_regulator_data
*
drvdata
=
rdev_get_drvdata
(
rdev
);
struct
pwm_state
pwm_state
;
unsigned
int
dutycycle
;
int
i
;
pwm_get_state
(
drvdata
->
pwm
,
&
pwm_state
);
dutycycle
=
pwm_get_relative_duty_cycle
(
&
pwm_state
,
100
);
for
(
i
=
0
;
i
<
rdev
->
desc
->
n_voltages
;
i
++
)
{
if
(
dutycycle
==
drvdata
->
duty_cycle_table
[
i
].
dutycycle
)
{
drvdata
->
state
=
i
;
return
;
}
}
}
static
int
pwm_regulator_get_voltage_sel
(
struct
regulator_dev
*
rdev
)
{
struct
pwm_regulator_data
*
drvdata
=
rdev_get_drvdata
(
rdev
);
if
(
drvdata
->
state
<
0
)
pwm_regulator_init_state
(
rdev
);
return
drvdata
->
state
;
}
...
...
@@ -59,16 +90,14 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
unsigned
selector
)
{
struct
pwm_regulator_data
*
drvdata
=
rdev_get_drvdata
(
rdev
);
struct
pwm_args
pargs
;
int
dutycycle
;
struct
pwm_state
pstate
;
int
ret
;
pwm_get_args
(
drvdata
->
pwm
,
&
pargs
);
dutycycle
=
(
pargs
.
period
*
drvdata
->
duty_cycle_table
[
selector
].
dutycycle
)
/
100
;
pwm_init_state
(
drvdata
->
pwm
,
&
pstate
);
pwm_set_relative_duty_cycle
(
&
pstate
,
drvdata
->
duty_cycle_table
[
selector
].
dutycycle
,
100
);
ret
=
pwm_
config
(
drvdata
->
pwm
,
dutycycle
,
pargs
.
period
);
ret
=
pwm_
apply_state
(
drvdata
->
pwm
,
&
pstate
);
if
(
ret
)
{
dev_err
(
&
rdev
->
dev
,
"Failed to configure PWM: %d
\n
"
,
ret
);
return
ret
;
...
...
@@ -94,6 +123,9 @@ static int pwm_regulator_enable(struct regulator_dev *dev)
{
struct
pwm_regulator_data
*
drvdata
=
rdev_get_drvdata
(
dev
);
if
(
drvdata
->
enb_gpio
)
gpiod_set_value_cansleep
(
drvdata
->
enb_gpio
,
1
);
return
pwm_enable
(
drvdata
->
pwm
);
}
...
...
@@ -103,6 +135,9 @@ static int pwm_regulator_disable(struct regulator_dev *dev)
pwm_disable
(
drvdata
->
pwm
);
if
(
drvdata
->
enb_gpio
)
gpiod_set_value_cansleep
(
drvdata
->
enb_gpio
,
0
);
return
0
;
}
...
...
@@ -110,64 +145,100 @@ static int pwm_regulator_is_enabled(struct regulator_dev *dev)
{
struct
pwm_regulator_data
*
drvdata
=
rdev_get_drvdata
(
dev
);
if
(
drvdata
->
enb_gpio
&&
!
gpiod_get_value_cansleep
(
drvdata
->
enb_gpio
))
return
false
;
return
pwm_is_enabled
(
drvdata
->
pwm
);
}
static
int
pwm_regulator_get_voltage
(
struct
regulator_dev
*
rdev
)
{
struct
pwm_regulator_data
*
drvdata
=
rdev_get_drvdata
(
rdev
);
unsigned
int
min_uV_duty
=
drvdata
->
continuous
.
min_uV_dutycycle
;
unsigned
int
max_uV_duty
=
drvdata
->
continuous
.
max_uV_dutycycle
;
unsigned
int
duty_unit
=
drvdata
->
continuous
.
dutycycle_unit
;
int
min_uV
=
rdev
->
constraints
->
min_uV
;
int
max_uV
=
rdev
->
constraints
->
max_uV
;
int
diff_uV
=
max_uV
-
min_uV
;
struct
pwm_state
pstate
;
unsigned
int
diff_duty
;
unsigned
int
voltage
;
pwm_get_state
(
drvdata
->
pwm
,
&
pstate
);
voltage
=
pwm_get_relative_duty_cycle
(
&
pstate
,
duty_unit
);
/*
* The dutycycle for min_uV might be greater than the one for max_uV.
* This is happening when the user needs an inversed polarity, but the
* PWM device does not support inversing it in hardware.
*/
if
(
max_uV_duty
<
min_uV_duty
)
{
voltage
=
min_uV_duty
-
voltage
;
diff_duty
=
min_uV_duty
-
max_uV_duty
;
}
else
{
voltage
=
voltage
-
min_uV_duty
;
diff_duty
=
max_uV_duty
-
min_uV_duty
;
}
voltage
=
DIV_ROUND_CLOSEST_ULL
((
u64
)
voltage
*
diff_uV
,
diff_duty
);
return
drvdata
->
volt
_uV
;
return
voltage
+
min
_uV
;
}
static
int
pwm_regulator_set_voltage
(
struct
regulator_dev
*
rdev
,
int
min_uV
,
int
max_uV
,
unsigned
*
selector
)
int
req_min_uV
,
int
req_
max_uV
,
unsigned
int
*
selector
)
{
struct
pwm_regulator_data
*
drvdata
=
rdev_get_drvdata
(
rdev
);
unsigned
int
min_uV_duty
=
drvdata
->
continuous
.
min_uV_dutycycle
;
unsigned
int
max_uV_duty
=
drvdata
->
continuous
.
max_uV_dutycycle
;
unsigned
int
duty_unit
=
drvdata
->
continuous
.
dutycycle_unit
;
unsigned
int
ramp_delay
=
rdev
->
constraints
->
ramp_delay
;
struct
pwm_args
pargs
;
unsigned
int
req_diff
=
min_uV
-
rdev
->
constraints
->
min_uV
;
unsigned
int
diff
;
unsigned
int
duty_pulse
;
u64
req_period
;
u32
rem
;
int
min_uV
=
rdev
->
constraints
->
min_uV
;
int
max_uV
=
rdev
->
constraints
->
max_uV
;
int
diff_uV
=
max_uV
-
min_uV
;
struct
pwm_state
pstate
;
int
old_uV
=
pwm_regulator_get_voltage
(
rdev
);
unsigned
int
diff_duty
;
unsigned
int
dutycycle
;
int
ret
;
pwm_get_args
(
drvdata
->
pwm
,
&
pargs
);
diff
=
rdev
->
constraints
->
max_uV
-
rdev
->
constraints
->
min_uV
;
pwm_init_state
(
drvdata
->
pwm
,
&
pstate
);
/* First try to find out if we get the iduty cycle time which is
* factor of PWM period time. If (request_diff_to_min * pwm_period)
* is perfect divided by voltage_range_diff then it is possible to
* get duty cycle time which is factor of PWM period. This will help
* to get output voltage nearer to requested value as there is no
* calculation loss.
/*
* The dutycycle for min_uV might be greater than the one for max_uV.
* This is happening when the user needs an inversed polarity, but the
* PWM device does not support inversing it in hardware.
*/
req_period
=
req_diff
*
pargs
.
period
;
div_u64_rem
(
req_period
,
diff
,
&
rem
);
if
(
!
rem
)
{
do_div
(
req_period
,
diff
);
duty_pulse
=
(
unsigned
int
)
req_period
;
}
else
{
duty_pulse
=
(
pargs
.
period
/
100
)
*
((
req_diff
*
100
)
/
diff
);
}
if
(
max_uV_duty
<
min_uV_duty
)
diff_duty
=
min_uV_duty
-
max_uV_duty
;
else
diff_duty
=
max_uV_duty
-
min_uV_duty
;
dutycycle
=
DIV_ROUND_CLOSEST_ULL
((
u64
)(
req_min_uV
-
min_uV
)
*
diff_duty
,
diff_uV
);
if
(
max_uV_duty
<
min_uV_duty
)
dutycycle
=
min_uV_duty
-
dutycycle
;
else
dutycycle
=
min_uV_duty
+
dutycycle
;
pwm_set_relative_duty_cycle
(
&
pstate
,
dutycycle
,
duty_unit
);
ret
=
pwm_
config
(
drvdata
->
pwm
,
duty_pulse
,
pargs
.
period
);
ret
=
pwm_
apply_state
(
drvdata
->
pwm
,
&
pstate
);
if
(
ret
)
{
dev_err
(
&
rdev
->
dev
,
"Failed to configure PWM: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
pwm_enable
(
drvdata
->
pwm
);
if
(
ret
)
{
dev_err
(
&
rdev
->
dev
,
"Failed to enable PWM: %d
\n
"
,
ret
);
return
ret
;
}
drvdata
->
volt_uV
=
min_uV
;
if
((
ramp_delay
==
0
)
||
!
pwm_regulator_is_enabled
(
rdev
))
return
0
;
/* Delay required by PWM regulator to settle to the new voltage */
usleep_range
(
ramp_delay
,
ramp_delay
+
1000
);
/* Ramp delay is in uV/uS. Adjust to uS and delay */
ramp_delay
=
DIV_ROUND_UP
(
abs
(
req_min_uV
-
old_uV
),
ramp_delay
);
usleep_range
(
ramp_delay
,
ramp_delay
+
DIV_ROUND_UP
(
ramp_delay
,
10
));
return
0
;
}
...
...
@@ -226,6 +297,7 @@ static int pwm_regulator_init_table(struct platform_device *pdev,
return
ret
;
}
drvdata
->
state
=
-
EINVAL
;
drvdata
->
duty_cycle_table
=
duty_cycle_table
;
memcpy
(
&
drvdata
->
ops
,
&
pwm_regulator_voltage_table_ops
,
sizeof
(
drvdata
->
ops
));
...
...
@@ -238,11 +310,28 @@ static int pwm_regulator_init_table(struct platform_device *pdev,
static
int
pwm_regulator_init_continuous
(
struct
platform_device
*
pdev
,
struct
pwm_regulator_data
*
drvdata
)
{
u32
dutycycle_range
[
2
]
=
{
0
,
100
};
u32
dutycycle_unit
=
100
;
memcpy
(
&
drvdata
->
ops
,
&
pwm_regulator_voltage_continuous_ops
,
sizeof
(
drvdata
->
ops
));
drvdata
->
desc
.
ops
=
&
drvdata
->
ops
;
drvdata
->
desc
.
continuous_voltage_range
=
true
;
of_property_read_u32_array
(
pdev
->
dev
.
of_node
,
"pwm-dutycycle-range"
,
dutycycle_range
,
2
);
of_property_read_u32
(
pdev
->
dev
.
of_node
,
"pwm-dutycycle-unit"
,
&
dutycycle_unit
);
if
(
dutycycle_range
[
0
]
>
dutycycle_unit
||
dutycycle_range
[
1
]
>
dutycycle_unit
)
return
-
EINVAL
;
drvdata
->
continuous
.
dutycycle_unit
=
dutycycle_unit
;
drvdata
->
continuous
.
min_uV_dutycycle
=
dutycycle_range
[
0
];
drvdata
->
continuous
.
max_uV_dutycycle
=
dutycycle_range
[
1
];
return
0
;
}
...
...
@@ -253,6 +342,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
struct
regulator_dev
*
regulator
;
struct
regulator_config
config
=
{
};
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
enum
gpiod_flags
gpio_flags
;
int
ret
;
if
(
!
np
)
{
...
...
@@ -290,11 +380,21 @@ static int pwm_regulator_probe(struct platform_device *pdev)
return
ret
;
}
/*
* FIXME: pwm_apply_args() should be removed when switching to the
* atomic PWM API.
*/
pwm_apply_args
(
drvdata
->
pwm
);
if
(
init_data
->
constraints
.
boot_on
||
init_data
->
constraints
.
always_on
)
gpio_flags
=
GPIOD_OUT_HIGH
;
else
gpio_flags
=
GPIOD_OUT_LOW
;
drvdata
->
enb_gpio
=
devm_gpiod_get_optional
(
&
pdev
->
dev
,
"enable"
,
gpio_flags
);
if
(
IS_ERR
(
drvdata
->
enb_gpio
))
{
ret
=
PTR_ERR
(
drvdata
->
enb_gpio
);
dev_err
(
&
pdev
->
dev
,
"Failed to get enable GPIO: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
pwm_adjust_config
(
drvdata
->
pwm
);
if
(
ret
)
return
ret
;
regulator
=
devm_regulator_register
(
&
pdev
->
dev
,
&
drvdata
->
desc
,
&
config
);
...
...
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