Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
OpenBMC Firmware
talos-obmc-linux
Commits
53de7c26
Commit
53de7c26
authored
8 years ago
by
Thierry Reding
Browse files
Options
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:
...
...
This diff is collapsed.
Click to expand it.
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
);
...
...
This diff is collapsed.
Click to expand it.
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