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
97512cea
Commit
97512cea
authored
Apr 13, 2017
by
Thierry Reding
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-4.12/drivers' into for-next
parents
45ce1d23
46fa8bc0
Changes
10
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
685 additions
and
309 deletions
+685
-309
Documentation/devicetree/bindings/pwm/atmel-pwm.txt
Documentation/devicetree/bindings/pwm/atmel-pwm.txt
+1
-0
Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
+45
-0
Documentation/devicetree/bindings/pwm/pwm-mediatek.txt
Documentation/devicetree/bindings/pwm/pwm-mediatek.txt
+34
-0
drivers/pwm/Kconfig
drivers/pwm/Kconfig
+9
-0
drivers/pwm/Makefile
drivers/pwm/Makefile
+1
-0
drivers/pwm/pwm-atmel-hlcdc.c
drivers/pwm/pwm-atmel-hlcdc.c
+135
-125
drivers/pwm/pwm-atmel.c
drivers/pwm/pwm-atmel.c
+132
-144
drivers/pwm/pwm-mediatek.c
drivers/pwm/pwm-mediatek.c
+219
-0
drivers/pwm/pwm-pca9685.c
drivers/pwm/pwm-pca9685.c
+79
-33
drivers/pwm/pwm-tegra.c
drivers/pwm/pwm-tegra.c
+30
-7
No files found.
Documentation/devicetree/bindings/pwm/atmel-pwm.txt
View file @
97512cea
...
...
@@ -4,6 +4,7 @@ Required properties:
- compatible: should be one of:
- "atmel,at91sam9rl-pwm"
- "atmel,sama5d3-pwm"
- "atmel,sama5d2-pwm"
- reg: physical base address and length of the controller's registers
- #pwm-cells: Should be 3. See pwm.txt in this directory for a
description of the cells format.
...
...
Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
View file @
97512cea
...
...
@@ -19,6 +19,19 @@ Required properties:
- reset-names: Must include the following entries:
- pwm
Optional properties:
============================
In some of the interface like PWM based regulator device, it is required
to configure the pins differently in different states, especially in suspend
state of the system. The configuration of pin is provided via the pinctrl
DT node as detailed in the pinctrl DT binding document
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
The PWM node will have following optional properties.
pinctrl-names: Pin state names. Must be "default" and "sleep".
pinctrl-0: phandle for the default/active state of pin configurations.
pinctrl-1: phandle for the sleep state of pin configurations.
Example:
pwm: pwm@7000a000 {
...
...
@@ -29,3 +42,35 @@ Example:
resets = <&tegra_car 17>;
reset-names = "pwm";
};
Example with the pin configuration for suspend and resume:
=========================================================
Suppose pin PE7 (On Tegra210) interfaced with the regulator device and
it requires PWM output to be tristated when system enters suspend.
Following will be DT binding to achieve this:
#include <dt-bindings/pinctrl/pinctrl-tegra.h>
pinmux@700008d4 {
pwm_active_state: pwm_active_state {
pe7 {
nvidia,pins = "pe7";
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
};
pwm_sleep_state: pwm_sleep_state {
pe7 {
nvidia,pins = "pe7";
nvidia,tristate = <TEGRA_PIN_ENABLE>;
};
};
};
pwm@7000a000 {
/* Mandatory PWM properties */
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pwm_active_state>;
pinctrl-1 = <&pwm_sleep_state>;
};
Documentation/devicetree/bindings/pwm/pwm-mediatek.txt
0 → 100644
View file @
97512cea
MediaTek PWM controller
Required properties:
- compatible: should be "mediatek,<name>-pwm":
- "mediatek,mt7623-pwm": found on mt7623 SoC.
- reg: physical base address and length of the controller's registers.
- #pwm-cells: must be 2. See pwm.txt in this directory for a description of
the cell format.
- clocks: phandle and clock specifier of the PWM reference clock.
- clock-names: must contain the following:
- "top": the top clock generator
- "main": clock used by the PWM core
- "pwm1-5": the five per PWM clocks
- pinctrl-names: Must contain a "default" entry.
- pinctrl-0: One property must exist for each entry in pinctrl-names.
See pinctrl/pinctrl-bindings.txt for details of the property values.
Example:
pwm0: pwm@11006000 {
compatible = "mediatek,mt7623-pwm";
reg = <0 0x11006000 0 0x1000>;
#pwm-cells = <2>;
clocks = <&topckgen CLK_TOP_PWM_SEL>,
<&pericfg CLK_PERI_PWM>,
<&pericfg CLK_PERI_PWM1>,
<&pericfg CLK_PERI_PWM2>,
<&pericfg CLK_PERI_PWM3>,
<&pericfg CLK_PERI_PWM4>,
<&pericfg CLK_PERI_PWM5>;
clock-names = "top", "main", "pwm1", "pwm2",
"pwm3", "pwm4", "pwm5";
pinctrl-names = "default";
pinctrl-0 = <&pwm0_pins>;
};
drivers/pwm/Kconfig
View file @
97512cea
...
...
@@ -293,6 +293,15 @@ config PWM_MTK_DISP
To compile this driver as a module, choose M here: the module
will be called pwm-mtk-disp.
config PWM_MEDIATEK
tristate "MediaTek PWM support"
depends on ARCH_MEDIATEK || COMPILE_TEST
help
Generic PWM framework driver for Mediatek ARM SoC.
To compile this driver as a module, choose M here: the module
will be called pwm-mxs.
config PWM_MXS
tristate "Freescale MXS PWM support"
depends on ARCH_MXS && OF
...
...
drivers/pwm/Makefile
View file @
97512cea
...
...
@@ -26,6 +26,7 @@ obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
obj-$(CONFIG_PWM_LPSS_PCI)
+=
pwm-lpss-pci.o
obj-$(CONFIG_PWM_LPSS_PLATFORM)
+=
pwm-lpss-platform.o
obj-$(CONFIG_PWM_MESON)
+=
pwm-meson.o
obj-$(CONFIG_PWM_MEDIATEK)
+=
pwm-mediatek.o
obj-$(CONFIG_PWM_MTK_DISP)
+=
pwm-mtk-disp.o
obj-$(CONFIG_PWM_MXS)
+=
pwm-mxs.o
obj-$(CONFIG_PWM_OMAP_DMTIMER)
+=
pwm-omap-dmtimer.o
...
...
drivers/pwm/pwm-atmel-hlcdc.c
View file @
97512cea
...
...
@@ -49,14 +49,17 @@ static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip)
return
container_of
(
chip
,
struct
atmel_hlcdc_pwm
,
chip
);
}
static
int
atmel_hlcdc_pwm_config
(
struct
pwm_chip
*
c
,
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
static
int
atmel_hlcdc_pwm_apply
(
struct
pwm_chip
*
c
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
atmel_hlcdc_pwm
*
chip
=
to_atmel_hlcdc_pwm
(
c
);
struct
atmel_hlcdc
*
hlcdc
=
chip
->
hlcdc
;
unsigned
int
status
;
int
ret
;
if
(
state
->
enabled
)
{
struct
clk
*
new_clk
=
hlcdc
->
slow_clk
;
u64
pwmcval
=
duty_ns
*
256
;
u64
pwmcval
=
state
->
duty_cycle
*
256
;
unsigned
long
clk_freq
;
u64
clk_period_ns
;
u32
pwmcfg
;
...
...
@@ -73,7 +76,7 @@ static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
/* Errata: cannot use slow clk on some IP revisions */
if
((
chip
->
errata
&&
chip
->
errata
->
slow_clk_erratum
)
||
clk_period_ns
>
period_ns
)
{
clk_period_ns
>
state
->
period
)
{
new_clk
=
hlcdc
->
sys_clk
;
clk_freq
=
clk_get_rate
(
new_clk
);
if
(
!
clk_freq
)
...
...
@@ -85,10 +88,11 @@ static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
for
(
pres
=
0
;
pres
<=
ATMEL_HLCDC_PWMPS_MAX
;
pres
++
)
{
/* Errata: cannot divide by 1 on some IP revisions */
if
(
!
pres
&&
chip
->
errata
&&
chip
->
errata
->
div1_clk_erratum
)
if
(
!
pres
&&
chip
->
errata
&&
chip
->
errata
->
div1_clk_erratum
)
continue
;
if
((
clk_period_ns
<<
pres
)
>=
period_ns
)
if
((
clk_period_ns
<<
pres
)
>=
state
->
period
)
break
;
}
...
...
@@ -111,18 +115,20 @@ static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
if
(
new_clk
==
hlcdc
->
sys_clk
)
gencfg
=
ATMEL_HLCDC_CLKPWMSEL
;
ret
=
regmap_update_bits
(
hlcdc
->
regmap
,
ATMEL_HLCDC_CFG
(
0
),
ATMEL_HLCDC_CLKPWMSEL
,
gencfg
);
ret
=
regmap_update_bits
(
hlcdc
->
regmap
,
ATMEL_HLCDC_CFG
(
0
),
ATMEL_HLCDC_CLKPWMSEL
,
gencfg
);
if
(
ret
)
return
ret
;
}
do_div
(
pwmcval
,
period_ns
);
do_div
(
pwmcval
,
state
->
period
);
/*
* The PWM duty cycle is configurable from 0/256 to 255/256 of the
* period cycle. Hence we can't set a duty cycle occupying the
*
whole period cycle if we're asked to.
* The PWM duty cycle is configurable from 0/256 to 255/256 of
* the period cycle. Hence we can't set a duty cycle occupying
* the
whole period cycle if we're asked to.
* Set it to 255 if pwmcval is greater than 256.
*/
if
(
pwmcval
>
255
)
...
...
@@ -130,81 +136,50 @@ static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
pwmcfg
|=
ATMEL_HLCDC_PWMCVAL
(
pwmcval
);
return
regmap_update_bits
(
hlcdc
->
regmap
,
ATMEL_HLCDC_CFG
(
6
),
if
(
state
->
polarity
==
PWM_POLARITY_NORMAL
)
pwmcfg
|=
ATMEL_HLCDC_PWMPOL
;
ret
=
regmap_update_bits
(
hlcdc
->
regmap
,
ATMEL_HLCDC_CFG
(
6
),
ATMEL_HLCDC_PWMCVAL_MASK
|
ATMEL_HLCDC_PWMPS_MASK
,
ATMEL_HLCDC_PWMPS_MASK
|
ATMEL_HLCDC_PWMPOL
,
pwmcfg
);
}
static
int
atmel_hlcdc_pwm_set_polarity
(
struct
pwm_chip
*
c
,
struct
pwm_device
*
pwm
,
enum
pwm_polarity
polarity
)
{
struct
atmel_hlcdc_pwm
*
chip
=
to_atmel_hlcdc_pwm
(
c
);
struct
atmel_hlcdc
*
hlcdc
=
chip
->
hlcdc
;
u32
cfg
=
0
;
if
(
polarity
==
PWM_POLARITY_NORMAL
)
cfg
=
ATMEL_HLCDC_PWMPOL
;
return
regmap_update_bits
(
hlcdc
->
regmap
,
ATMEL_HLCDC_CFG
(
6
),
ATMEL_HLCDC_PWMPOL
,
cfg
);
}
static
int
atmel_hlcdc_pwm_enable
(
struct
pwm_chip
*
c
,
struct
pwm_device
*
pwm
)
{
struct
atmel_hlcdc_pwm
*
chip
=
to_atmel_hlcdc_pwm
(
c
);
struct
atmel_hlcdc
*
hlcdc
=
chip
->
hlcdc
;
u32
status
;
int
ret
;
ret
=
regmap_write
(
hlcdc
->
regmap
,
ATMEL_HLCDC_EN
,
ATMEL_HLCDC_PWM
);
if
(
ret
)
return
ret
;
while
(
true
)
{
ret
=
regmap_read
(
hlcdc
->
regmap
,
ATMEL_HLCDC_SR
,
&
status
);
ret
=
regmap_write
(
hlcdc
->
regmap
,
ATMEL_HLCDC_EN
,
ATMEL_HLCDC_PWM
);
if
(
ret
)
return
ret
;
if
((
status
&
ATMEL_HLCDC_PWM
)
!=
0
)
break
;
usleep_range
(
1
,
10
);
}
return
0
;
}
static
void
atmel_hlcdc_pwm_disable
(
struct
pwm_chip
*
c
,
struct
pwm_device
*
pwm
)
{
struct
atmel_hlcdc_pwm
*
chip
=
to_atmel_hlcdc_pwm
(
c
);
struct
atmel_hlcdc
*
hlcdc
=
chip
->
hlcdc
;
u32
status
;
int
ret
;
ret
=
regmap_write
(
hlcdc
->
regmap
,
ATMEL_HLCDC_DIS
,
ATMEL_HLCDC_PWM
);
ret
=
regmap_read_poll_timeout
(
hlcdc
->
regmap
,
ATMEL_HLCDC_SR
,
status
,
status
&
ATMEL_HLCDC_PWM
,
10
,
0
);
if
(
ret
)
return
;
while
(
true
)
{
ret
=
regmap_read
(
hlcdc
->
regmap
,
ATMEL_HLCDC_SR
,
&
status
);
return
ret
;
}
else
{
ret
=
regmap_write
(
hlcdc
->
regmap
,
ATMEL_HLCDC_DIS
,
ATMEL_HLCDC_PWM
);
if
(
ret
)
return
;
return
ret
;
if
((
status
&
ATMEL_HLCDC_PWM
)
==
0
)
break
;
ret
=
regmap_read_poll_timeout
(
hlcdc
->
regmap
,
ATMEL_HLCDC_SR
,
status
,
!
(
status
&
ATMEL_HLCDC_PWM
),
10
,
0
);
if
(
ret
)
return
ret
;
usleep_range
(
1
,
10
);
clk_disable_unprepare
(
chip
->
cur_clk
);
chip
->
cur_clk
=
NULL
;
}
return
0
;
}
static
const
struct
pwm_ops
atmel_hlcdc_pwm_ops
=
{
.
config
=
atmel_hlcdc_pwm_config
,
.
set_polarity
=
atmel_hlcdc_pwm_set_polarity
,
.
enable
=
atmel_hlcdc_pwm_enable
,
.
disable
=
atmel_hlcdc_pwm_disable
,
.
apply
=
atmel_hlcdc_pwm_apply
,
.
owner
=
THIS_MODULE
,
};
...
...
@@ -216,6 +191,40 @@ static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = {
.
div1_clk_erratum
=
true
,
};
#ifdef CONFIG_PM_SLEEP
static
int
atmel_hlcdc_pwm_suspend
(
struct
device
*
dev
)
{
struct
atmel_hlcdc_pwm
*
chip
=
dev_get_drvdata
(
dev
);
/* Keep the periph clock enabled if the PWM is still running. */
if
(
pwm_is_enabled
(
&
chip
->
chip
.
pwms
[
0
]))
clk_disable_unprepare
(
chip
->
hlcdc
->
periph_clk
);
return
0
;
}
static
int
atmel_hlcdc_pwm_resume
(
struct
device
*
dev
)
{
struct
atmel_hlcdc_pwm
*
chip
=
dev_get_drvdata
(
dev
);
struct
pwm_state
state
;
int
ret
;
pwm_get_state
(
&
chip
->
chip
.
pwms
[
0
],
&
state
);
/* Re-enable the periph clock it was stopped during suspend. */
if
(
!
state
.
enabled
)
{
ret
=
clk_prepare_enable
(
chip
->
hlcdc
->
periph_clk
);
if
(
ret
)
return
ret
;
}
return
atmel_hlcdc_pwm_apply
(
&
chip
->
chip
,
&
chip
->
chip
.
pwms
[
0
],
&
state
);
}
#endif
static
SIMPLE_DEV_PM_OPS
(
atmel_hlcdc_pwm_pm_ops
,
atmel_hlcdc_pwm_suspend
,
atmel_hlcdc_pwm_resume
);
static
const
struct
of_device_id
atmel_hlcdc_dt_ids
[]
=
{
{
.
compatible
=
"atmel,at91sam9n12-hlcdc"
,
...
...
@@ -305,6 +314,7 @@ static struct platform_driver atmel_hlcdc_pwm_driver = {
.
driver
=
{
.
name
=
"atmel-hlcdc-pwm"
,
.
of_match_table
=
atmel_hlcdc_pwm_dt_ids
,
.
pm
=
&
atmel_hlcdc_pwm_pm_ops
,
},
.
probe
=
atmel_hlcdc_pwm_probe
,
.
remove
=
atmel_hlcdc_pwm_remove
,
...
...
drivers/pwm/pwm-atmel.c
View file @
97512cea
This diff is collapsed.
Click to expand it.
drivers/pwm/pwm-mediatek.c
0 → 100644
View file @
97512cea
/*
* Mediatek Pulse Width Modulator driver
*
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/types.h>
/* PWM registers and bits definitions */
#define PWMCON 0x00
#define PWMHDUR 0x04
#define PWMLDUR 0x08
#define PWMGDUR 0x0c
#define PWMWAVENUM 0x28
#define PWMDWIDTH 0x2c
#define PWMTHRES 0x30
enum
{
MTK_CLK_MAIN
=
0
,
MTK_CLK_TOP
,
MTK_CLK_PWM1
,
MTK_CLK_PWM2
,
MTK_CLK_PWM3
,
MTK_CLK_PWM4
,
MTK_CLK_PWM5
,
MTK_CLK_MAX
,
};
static
const
char
*
const
mtk_pwm_clk_name
[]
=
{
"main"
,
"top"
,
"pwm1"
,
"pwm2"
,
"pwm3"
,
"pwm4"
,
"pwm5"
};
/**
* struct mtk_pwm_chip - struct representing PWM chip
* @chip: linux PWM chip representation
* @regs: base address of PWM chip
* @clks: list of clocks
*/
struct
mtk_pwm_chip
{
struct
pwm_chip
chip
;
void
__iomem
*
regs
;
struct
clk
*
clks
[
MTK_CLK_MAX
];
};
static
inline
struct
mtk_pwm_chip
*
to_mtk_pwm_chip
(
struct
pwm_chip
*
chip
)
{
return
container_of
(
chip
,
struct
mtk_pwm_chip
,
chip
);
}
static
inline
u32
mtk_pwm_readl
(
struct
mtk_pwm_chip
*
chip
,
unsigned
int
num
,
unsigned
int
offset
)
{
return
readl
(
chip
->
regs
+
0x10
+
(
num
*
0x40
)
+
offset
);
}
static
inline
void
mtk_pwm_writel
(
struct
mtk_pwm_chip
*
chip
,
unsigned
int
num
,
unsigned
int
offset
,
u32
value
)
{
writel
(
value
,
chip
->
regs
+
0x10
+
(
num
*
0x40
)
+
offset
);
}
static
int
mtk_pwm_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
{
struct
mtk_pwm_chip
*
pc
=
to_mtk_pwm_chip
(
chip
);
struct
clk
*
clk
=
pc
->
clks
[
MTK_CLK_PWM1
+
pwm
->
hwpwm
];
u32
resolution
,
clkdiv
=
0
;
resolution
=
NSEC_PER_SEC
/
clk_get_rate
(
clk
);
while
(
period_ns
/
resolution
>
8191
)
{
resolution
*=
2
;
clkdiv
++
;
}
if
(
clkdiv
>
7
)
return
-
EINVAL
;
mtk_pwm_writel
(
pc
,
pwm
->
hwpwm
,
PWMCON
,
BIT
(
15
)
|
BIT
(
3
)
|
clkdiv
);
mtk_pwm_writel
(
pc
,
pwm
->
hwpwm
,
PWMDWIDTH
,
period_ns
/
resolution
);
mtk_pwm_writel
(
pc
,
pwm
->
hwpwm
,
PWMTHRES
,
duty_ns
/
resolution
);
return
0
;
}
static
int
mtk_pwm_enable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
mtk_pwm_chip
*
pc
=
to_mtk_pwm_chip
(
chip
);
u32
value
;
int
ret
;
ret
=
clk_prepare
(
pc
->
clks
[
MTK_CLK_PWM1
+
pwm
->
hwpwm
]);
if
(
ret
<
0
)
return
ret
;
value
=
readl
(
pc
->
regs
);
value
|=
BIT
(
pwm
->
hwpwm
);
writel
(
value
,
pc
->
regs
);
return
0
;
}
static
void
mtk_pwm_disable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
mtk_pwm_chip
*
pc
=
to_mtk_pwm_chip
(
chip
);
u32
value
;
value
=
readl
(
pc
->
regs
);
value
&=
~
BIT
(
pwm
->
hwpwm
);
writel
(
value
,
pc
->
regs
);
clk_unprepare
(
pc
->
clks
[
MTK_CLK_PWM1
+
pwm
->
hwpwm
]);
}
static
const
struct
pwm_ops
mtk_pwm_ops
=
{
.
config
=
mtk_pwm_config
,
.
enable
=
mtk_pwm_enable
,
.
disable
=
mtk_pwm_disable
,
.
owner
=
THIS_MODULE
,
};
static
int
mtk_pwm_probe
(
struct
platform_device
*
pdev
)
{
struct
mtk_pwm_chip
*
pc
;
struct
resource
*
res
;
unsigned
int
i
;
int
ret
;
pc
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
pc
),
GFP_KERNEL
);
if
(
!
pc
)
return
-
ENOMEM
;
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
pc
->
regs
=
devm_ioremap_resource
(
&
pdev
->
dev
,
res
);
if
(
IS_ERR
(
pc
->
regs
))
return
PTR_ERR
(
pc
->
regs
);
for
(
i
=
0
;
i
<
MTK_CLK_MAX
;
i
++
)
{
pc
->
clks
[
i
]
=
devm_clk_get
(
&
pdev
->
dev
,
mtk_pwm_clk_name
[
i
]);
if
(
IS_ERR
(
pc
->
clks
[
i
]))
return
PTR_ERR
(
pc
->
clks
[
i
]);
}
ret
=
clk_prepare
(
pc
->
clks
[
MTK_CLK_TOP
]);
if
(
ret
<
0
)
return
ret
;
ret
=
clk_prepare
(
pc
->
clks
[
MTK_CLK_MAIN
]);
if
(
ret
<
0
)
goto
disable_clk_top
;
platform_set_drvdata
(
pdev
,
pc
);
pc
->
chip
.
dev
=
&
pdev
->
dev
;
pc
->
chip
.
ops
=
&
mtk_pwm_ops
;
pc
->
chip
.
base
=
-
1
;
pc
->
chip
.
npwm
=
5
;
ret
=
pwmchip_add
(
&
pc
->
chip
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"pwmchip_add() failed: %d
\n
"
,
ret
);
goto
disable_clk_main
;
}
return
0
;
disable_clk_main:
clk_unprepare
(
pc
->
clks
[
MTK_CLK_MAIN
]);
disable_clk_top:
clk_unprepare
(
pc
->
clks
[
MTK_CLK_TOP
]);
return
ret
;
}
static
int
mtk_pwm_remove
(
struct
platform_device
*
pdev
)
{
struct
mtk_pwm_chip
*
pc
=
platform_get_drvdata
(
pdev
);
unsigned
int
i
;
for
(
i
=
0
;
i
<
pc
->
chip
.
npwm
;
i
++
)
pwm_disable
(
&
pc
->
chip
.
pwms
[
i
]);
return
pwmchip_remove
(
&
pc
->
chip
);
}
static
const
struct
of_device_id
mtk_pwm_of_match
[]
=
{
{
.
compatible
=
"mediatek,mt7623-pwm"
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
mtk_pwm_of_match
);
static
struct
platform_driver
mtk_pwm_driver
=
{
.
driver
=
{
.
name
=
"mtk-pwm"
,
.
of_match_table
=
mtk_pwm_of_match
,
},
.
probe
=
mtk_pwm_probe
,
.
remove
=
mtk_pwm_remove
,
};
module_platform_driver
(
mtk_pwm_driver
);
MODULE_AUTHOR
(
"John Crispin <blogic@openwrt.org>"
);
MODULE_ALIAS
(
"platform:mtk-pwm"
);
MODULE_LICENSE
(
"GPL"
);
drivers/pwm/pwm-pca9685.c
View file @
97512cea
...
...
@@ -30,6 +30,7 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
/*
* Because the PCA9685 has only one prescaler per chip, changing the period of
...
...
@@ -79,7 +80,6 @@
struct
pca9685
{
struct
pwm_chip
chip
;
struct
regmap
*
regmap
;
int
active_cnt
;
int
duty_ns
;
int
period_ns
;
#if IS_ENABLED(CONFIG_GPIOLIB)
...
...
@@ -111,20 +111,10 @@ static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
pwm_set_chip_data
(
pwm
,
(
void
*
)
1
);
mutex_unlock
(
&
pca
->
lock
);
pm_runtime_get_sync
(
pca
->
chip
.
dev
);
return
0
;
}
static
void
pca9685_pwm_gpio_free
(
struct
gpio_chip
*
gpio
,
unsigned
int
offset
)
{
struct
pca9685
*
pca
=
gpiochip_get_data
(
gpio
);
struct
pwm_device
*
pwm
;
mutex_lock
(
&
pca
->
lock
);
pwm
=
&
pca
->
chip
.
pwms
[
offset
];
pwm_set_chip_data
(
pwm
,
NULL
);
mutex_unlock
(
&
pca
->
lock
);
}
static
bool
pca9685_pwm_is_gpio
(
struct
pca9685
*
pca
,
struct
pwm_device
*
pwm
)
{
bool
is_gpio
=
false
;
...
...
@@ -177,6 +167,19 @@ static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset,
regmap_write
(
pca
->
regmap
,
LED_N_ON_H
(
pwm
->
hwpwm
),
on
);
}
static
void
pca9685_pwm_gpio_free
(
struct
gpio_chip
*
gpio
,
unsigned
int
offset
)
{
struct
pca9685
*
pca
=
gpiochip_get_data
(
gpio
);
struct
pwm_device
*
pwm
;
pca9685_pwm_gpio_set
(
gpio
,
offset
,
0
);
pm_runtime_put
(
pca
->
chip
.
dev
);
mutex_lock
(
&
pca
->
lock
);
pwm
=
&
pca
->
chip
.
pwms
[
offset
];
pwm_set_chip_data
(
pwm
,
NULL
);
mutex_unlock
(
&
pca
->
lock
);
}
static
int
pca9685_pwm_gpio_get_direction
(
struct
gpio_chip
*
chip
,
unsigned
int
offset
)
{
...
...
@@ -238,6 +241,16 @@ static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
}
#endif
static
void
pca9685_set_sleep_mode
(
struct
pca9685
*
pca
,
int
sleep
)
{
regmap_update_bits
(
pca
->
regmap
,
PCA9685_MODE1
,
MODE1_SLEEP
,
sleep
?
MODE1_SLEEP
:
0
);
if
(
!
sleep
)
{
/* Wait 500us for the oscillator to be back up */
udelay
(
500
);
}
}
static
int
pca9685_pwm_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
{
...
...
@@ -252,19 +265,20 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
if
(
prescale
>=
PCA9685_PRESCALE_MIN
&&
prescale
<=
PCA9685_PRESCALE_MAX
)
{
/*
* putting the chip briefly into SLEEP mode
* at this point won't interfere with the
* pm_runtime framework, because the pm_runtime
* state is guaranteed active here.
*/
/* Put chip into sleep mode */
regmap_update_bits
(
pca
->
regmap
,
PCA9685_MODE1
,
MODE1_SLEEP
,
MODE1_SLEEP
);
pca9685_set_sleep_mode
(
pca
,
1
);
/* Change the chip-wide output frequency */
regmap_write
(
pca
->
regmap
,
PCA9685_PRESCALE
,
prescale
);
/* Wake the chip up */
regmap_update_bits
(
pca
->
regmap
,
PCA9685_MODE1
,
MODE1_SLEEP
,
0x0
);
/* Wait 500us for the oscillator to be back up */
udelay
(
500
);
pca9685_set_sleep_mode
(
pca
,
0
);
pca
->
period_ns
=
period_ns
;
}
else
{
...
...
@@ -406,21 +420,15 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
if
(
pca9685_pwm_is_gpio
(
pca
,
pwm
))
return
-
EBUSY
;
if
(
pca
->
active_cnt
++
==
0
)
return
regmap_update_bits
(
pca
->
regmap
,
PCA9685_MODE1
,
MODE1_SLEEP
,
0x0
);
pm_runtime_get_sync
(
chip
->
dev
);
return
0
;
}
static
void
pca9685_pwm_free
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
pca9685
*
pca
=
to_pca
(
chip
);
if
(
--
pca
->
active_cnt
==
0
)
regmap_update_bits
(
pca
->
regmap
,
PCA9685_MODE1
,
MODE1_SLEEP
,
MODE1_SLEEP
);
pca9685_pwm_disable
(
chip
,
pwm
);
pm_runtime_put
(
chip
->
dev
);
}
static
const
struct
pwm_ops
pca9685_pwm_ops
=
{
...
...
@@ -492,21 +500,53 @@ static int pca9685_pwm_probe(struct i2c_client *client,
return
ret
;
ret
=
pca9685_pwm_gpio_probe
(
pca
);
if
(
ret
<
0
)
if
(
ret
<
0
)
{
pwmchip_remove
(
&
pca
->
chip
);
return
ret
;
}
/* the chip comes out of power-up in the active state */
pm_runtime_set_active
(
&
client
->
dev
);
/*
* enable will put the chip into suspend, which is what we
* want as all outputs are disabled at this point
*/
pm_runtime_enable
(
&
client
->
dev
);
return
0
;
}
static
int
pca9685_pwm_remove
(
struct
i2c_client
*
client
)
{
struct
pca9685
*
pca
=
i2c_get_clientdata
(
client
);
int
ret
;
ret
=
pwmchip_remove
(
&
pca
->
chip
);
if
(
ret
)
return
ret
;
pm_runtime_disable
(
&
client
->
dev
);
return
0
;
}
#ifdef CONFIG_PM
static
int
pca9685_pwm_runtime_suspend
(
struct
device
*
dev
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
pca9685
*
pca
=
i2c_get_clientdata
(
client
);
pca9685_set_sleep_mode
(
pca
,
1
);
return
0
;
}
regmap_update_bits
(
pca
->
regmap
,
PCA9685_MODE1
,
MODE1_SLEEP
,
MODE1_SLEEP
);
static
int
pca9685_pwm_runtime_resume
(
struct
device
*
dev
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
pca9685
*
pca
=
i2c_get_clientdata
(
client
);
return
pwmchip_remove
(
&
pca
->
chip
);
pca9685_set_sleep_mode
(
pca
,
0
);
return
0
;
}
#endif
static
const
struct
i2c_device_id
pca9685_id
[]
=
{
{
"pca9685"
,
0
},
...
...
@@ -530,11 +570,17 @@ static const struct of_device_id pca9685_dt_ids[] = {
MODULE_DEVICE_TABLE
(
of
,
pca9685_dt_ids
);
#endif
static
const
struct
dev_pm_ops
pca9685_pwm_pm
=
{
SET_RUNTIME_PM_OPS
(
pca9685_pwm_runtime_suspend
,
pca9685_pwm_runtime_resume
,
NULL
)
};
static
struct
i2c_driver
pca9685_i2c_driver
=
{
.
driver
=
{
.
name
=
"pca9685-pwm"
,
.
acpi_match_table
=
ACPI_PTR
(
pca9685_acpi_ids
),
.
of_match_table
=
of_match_ptr
(
pca9685_dt_ids
),
.
pm
=
&
pca9685_pwm_pm
,
},
.
probe
=
pca9685_pwm_probe
,
.
remove
=
pca9685_pwm_remove
,
...
...
drivers/pwm/pwm-tegra.c
View file @
97512cea
...
...
@@ -29,6 +29,7 @@
#include <linux/of_device.h>
#include <linux/pwm.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/slab.h>
#include <linux/reset.h>
...
...
@@ -49,6 +50,8 @@ struct tegra_pwm_chip {
struct
clk
*
clk
;
struct
reset_control
*
rst
;
unsigned
long
clk_rate
;
void
__iomem
*
regs
;
const
struct
tegra_pwm_soc
*
soc
;
...
...
@@ -74,8 +77,8 @@ 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
=
duty_ns
;
unsigned
long
rate
,
hz
;
unsigned
long
long
c
=
duty_ns
,
hz
;
unsigned
long
rate
;
u32
val
=
0
;
int
err
;
...
...
@@ -85,8 +88,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* nearest integer during division.
*/
c
*=
(
1
<<
PWM_DUTY_WIDTH
);
c
+=
period_ns
/
2
;
do_div
(
c
,
period_ns
);
c
=
DIV_ROUND_CLOSEST_ULL
(
c
,
period_ns
);
val
=
(
u32
)
c
<<
PWM_DUTY_SHIFT
;
...
...
@@ -94,10 +96,11 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* 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
=
NSEC_PER_SEC
/
period_ns
;
rate
=
pc
->
clk_rate
>>
PWM_DUTY_WIDTH
;
rate
=
(
rate
+
(
hz
/
2
))
/
hz
;
/* Consider precision in PWM_SCALE_WIDTH rate calculation */
hz
=
DIV_ROUND_CLOSEST_ULL
(
100ULL
*
NSEC_PER_SEC
,
period_ns
);
rate
=
DIV_ROUND_CLOSEST_ULL
(
100ULL
*
rate
,
hz
);
/*
* Since the actual PWM divider is the register's frequency divider
...
...
@@ -198,6 +201,9 @@ static int tegra_pwm_probe(struct platform_device *pdev)
if
(
IS_ERR
(
pwm
->
clk
))
return
PTR_ERR
(
pwm
->
clk
);
/* Read PWM clock rate from source */
pwm
->
clk_rate
=
clk_get_rate
(
pwm
->
clk
);
pwm
->
rst
=
devm_reset_control_get
(
&
pdev
->
dev
,
"pwm"
);
if
(
IS_ERR
(
pwm
->
rst
))
{
ret
=
PTR_ERR
(
pwm
->
rst
);
...
...
@@ -253,6 +259,18 @@ static int tegra_pwm_remove(struct platform_device *pdev)
return
pwmchip_remove
(
&
pc
->
chip
);
}
#ifdef CONFIG_PM_SLEEP
static
int
tegra_pwm_suspend
(
struct
device
*
dev
)
{
return
pinctrl_pm_select_sleep_state
(
dev
);
}
static
int
tegra_pwm_resume
(
struct
device
*
dev
)
{
return
pinctrl_pm_select_default_state
(
dev
);
}
#endif
static
const
struct
tegra_pwm_soc
tegra20_pwm_soc
=
{
.
num_channels
=
4
,
};
...
...
@@ -269,10 +287,15 @@ static const struct of_device_id tegra_pwm_of_match[] = {
MODULE_DEVICE_TABLE
(
of
,
tegra_pwm_of_match
);
static
const
struct
dev_pm_ops
tegra_pwm_pm_ops
=
{
SET_SYSTEM_SLEEP_PM_OPS
(
tegra_pwm_suspend
,
tegra_pwm_resume
)
};
static
struct
platform_driver
tegra_pwm_driver
=
{
.
driver
=
{
.
name
=
"tegra-pwm"
,
.
of_match_table
=
tegra_pwm_of_match
,
.
pm
=
&
tegra_pwm_pm_ops
,
},
.
probe
=
tegra_pwm_probe
,
.
remove
=
tegra_pwm_remove
,
...
...
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