Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
97512cea
K
kernel_linux
项目概览
OpenHarmony
/
kernel_linux
上一次同步 4 年多
通知
15
Star
8
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
kernel_linux
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
97512cea
编写于
4月 13, 2017
作者:
T
Thierry Reding
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'for-4.12/drivers' into for-next
上级
45ce1d23
46fa8bc0
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
685 addition
and
309 deletion
+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
未找到文件。
Documentation/devicetree/bindings/pwm/atmel-pwm.txt
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
97512cea
...
...
@@ -49,172 +49,181 @@ 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
;
struct
clk
*
new_clk
=
hlcdc
->
slow_clk
;
u64
pwmcval
=
duty_ns
*
256
;
unsigned
long
clk_freq
;
u64
clk_period_ns
;
u32
pwmcfg
;
int
pres
;
if
(
!
chip
->
errata
||
!
chip
->
errata
->
slow_clk_erratum
)
{
clk_freq
=
clk_get_rate
(
new_clk
);
if
(
!
clk_freq
)
return
-
EINVAL
;
unsigned
int
status
;
int
ret
;
clk_period_ns
=
(
u64
)
NSEC_PER_SEC
*
256
;
do_div
(
clk_period_ns
,
clk_freq
);
}
if
(
state
->
enabled
)
{
struct
clk
*
new_clk
=
hlcdc
->
slow_clk
;
u64
pwmcval
=
state
->
duty_cycle
*
256
;
unsigned
long
clk_freq
;
u64
clk_period_ns
;
u32
pwmcfg
;
int
pres
;
if
(
!
chip
->
errata
||
!
chip
->
errata
->
slow_clk_erratum
)
{
clk_freq
=
clk_get_rate
(
new_clk
);
if
(
!
clk_freq
)
return
-
EINVAL
;
clk_period_ns
=
(
u64
)
NSEC_PER_SEC
*
256
;
do_div
(
clk_period_ns
,
clk_freq
);
}
/* Errata: cannot use slow clk on some IP revisions */
if
((
chip
->
errata
&&
chip
->
errata
->
slow_clk_erratum
)
||
clk_period_ns
>
state
->
period
)
{
new_clk
=
hlcdc
->
sys_clk
;
clk_freq
=
clk_get_rate
(
new_clk
);
if
(
!
clk_freq
)
return
-
EINVAL
;
clk_period_ns
=
(
u64
)
NSEC_PER_SEC
*
256
;
do_div
(
clk_period_ns
,
clk_freq
);
}
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
)
continue
;
if
((
clk_period_ns
<<
pres
)
>=
state
->
period
)
break
;
}
/* Errata: cannot use slow clk on some IP revisions */
if
((
chip
->
errata
&&
chip
->
errata
->
slow_clk_erratum
)
||
clk_period_ns
>
period_ns
)
{
new_clk
=
hlcdc
->
sys_clk
;
clk_freq
=
clk_get_rate
(
new_clk
);
if
(
!
clk_freq
)
if
(
pres
>
ATMEL_HLCDC_PWMPS_MAX
)
return
-
EINVAL
;
clk_period_ns
=
(
u64
)
NSEC_PER_SEC
*
256
;
do_div
(
clk_period_ns
,
clk_freq
);
}
pwmcfg
=
ATMEL_HLCDC_PWMPS
(
pres
);
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
)
continue
;
if
(
new_clk
!=
chip
->
cur_clk
)
{
u32
gencfg
=
0
;
int
ret
;
if
((
clk_period_ns
<<
pres
)
>=
period_ns
)
break
;
}
ret
=
clk_prepare_enable
(
new_clk
);
if
(
ret
)
return
ret
;
if
(
pres
>
ATMEL_HLCDC_PWMPS_MAX
)
return
-
EINVAL
;
clk_disable_unprepare
(
chip
->
cur_clk
);
chip
->
cur_clk
=
new_clk
;
pwmcfg
=
ATMEL_HLCDC_PWMPS
(
pres
);
if
(
new_clk
==
hlcdc
->
sys_clk
)
gencfg
=
ATMEL_HLCDC_CLKPWMSEL
;
if
(
new_clk
!=
chip
->
cur_clk
)
{
u32
gencfg
=
0
;
int
ret
;
ret
=
regmap_update_bits
(
hlcdc
->
regmap
,
ATMEL_HLCDC_CFG
(
0
),
ATMEL_HLCDC_CLKPWMSEL
,
gencfg
);
if
(
ret
)
return
ret
;
}
ret
=
clk_prepare_enable
(
new_clk
);
if
(
ret
)
return
ret
;
do_div
(
pwmcval
,
state
->
period
);
clk_disable_unprepare
(
chip
->
cur_clk
);
chip
->
cur_clk
=
new_clk
;
/*
* 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
)
pwmcval
=
255
;
if
(
new_clk
==
hlcdc
->
sys_clk
)
gencfg
=
ATMEL_HLCDC_CLKPWMSEL
;
pwmcfg
|=
ATMEL_HLCDC_PWMCVAL
(
pwmcval
);
ret
=
regmap_update_bits
(
hlcdc
->
regmap
,
ATMEL_HLCDC_CFG
(
0
),
ATMEL_HLCDC_CLKPWMSEL
,
gencfg
);
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_PWMPOL
,
pwmcfg
);
if
(
ret
)
return
ret
;
}
do_div
(
pwmcval
,
period_ns
);
/*
* 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
)
pwmcval
=
255
;
pwmcfg
|=
ATMEL_HLCDC_PWMCVAL
(
pwmcval
);
ret
=
regmap_write
(
hlcdc
->
regmap
,
ATMEL_HLCDC_EN
,
ATMEL_HLCDC_PWM
);
if
(
ret
)
return
ret
;
return
regmap_update_bits
(
hlcdc
->
regmap
,
ATMEL_HLCDC_CFG
(
6
),
ATMEL_HLCDC_PWMCVAL_MASK
|
ATMEL_HLCDC_PWMPS_MASK
,
pwmcfg
);
}
ret
=
regmap_read_poll_timeout
(
hlcdc
->
regmap
,
ATMEL_HLCDC_SR
,
status
,
status
&
ATMEL_HLCDC_PWM
,
10
,
0
);
if
(
ret
)
return
ret
;
}
else
{
ret
=
regmap_write
(
hlcdc
->
regmap
,
ATMEL_HLCDC_DIS
,
ATMEL_HLCDC_PWM
);
if
(
ret
)
return
ret
;
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
;
ret
=
regmap_read_poll_timeout
(
hlcdc
->
regmap
,
ATMEL_HLCDC_SR
,
status
,
!
(
status
&
ATMEL_HLCDC_PWM
),
10
,
0
);
if
(
ret
)
return
ret
;
if
(
polarity
==
PWM_POLARITY_NORMAL
)
cfg
=
ATMEL_HLCDC_PWMPOL
;
clk_disable_unprepare
(
chip
->
cur_clk
);
chip
->
cur_clk
=
NULL
;
}
return
regmap_update_bits
(
hlcdc
->
regmap
,
ATMEL_HLCDC_CFG
(
6
),
ATMEL_HLCDC_PWMPOL
,
cfg
);
return
0
;
}
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
;
static
const
struct
pwm_ops
atmel_hlcdc_pwm_ops
=
{
.
apply
=
atmel_hlcdc_pwm_apply
,
.
owner
=
THIS_MODULE
,
};
ret
=
regmap_write
(
hlcdc
->
regmap
,
ATMEL_HLCDC_EN
,
ATMEL_HLCDC_PWM
);
if
(
ret
)
return
ret
;
static
const
struct
atmel_hlcdc_pwm_errata
atmel_hlcdc_pwm_at91sam9x5_errata
=
{
.
slow_clk_erratum
=
true
,
}
;
while
(
true
)
{
ret
=
regmap_read
(
hlcdc
->
regmap
,
ATMEL_HLCDC_SR
,
&
status
);
if
(
ret
)
return
ret
;
static
const
struct
atmel_hlcdc_pwm_errata
atmel_hlcdc_pwm_sama5d3_errata
=
{
.
div1_clk_erratum
=
true
,
};
if
((
status
&
ATMEL_HLCDC_PWM
)
!=
0
)
break
;
#ifdef CONFIG_PM_SLEEP
static
int
atmel_hlcdc_pwm_suspend
(
struct
device
*
dev
)
{
struct
atmel_hlcdc_pwm
*
chip
=
dev_get_drvdata
(
dev
);
usleep_range
(
1
,
10
);
}
/* 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
void
atmel_hlcdc_pwm_disable
(
struct
pwm_chip
*
c
,
struct
pwm_device
*
pwm
)
static
int
atmel_hlcdc_pwm_resume
(
struct
device
*
dev
)
{
struct
atmel_hlcdc_pwm
*
chip
=
to_atmel_hlcdc_pwm
(
c
);
struct
atmel_hlcdc
*
hlcdc
=
chip
->
hlcdc
;
u32
status
;
struct
atmel_hlcdc_pwm
*
chip
=
dev_get_drvdata
(
dev
);
struct
pwm_state
state
;
int
ret
;
ret
=
regmap_write
(
hlcdc
->
regmap
,
ATMEL_HLCDC_DIS
,
ATMEL_HLCDC_PWM
);
if
(
ret
)
return
;
pwm_get_state
(
&
chip
->
chip
.
pwms
[
0
],
&
state
);
while
(
true
)
{
ret
=
regmap_read
(
hlcdc
->
regmap
,
ATMEL_HLCDC_SR
,
&
status
);
/* Re-enable the periph clock it was stopped during suspend. */
if
(
!
state
.
enabled
)
{
ret
=
clk_prepare_enable
(
chip
->
hlcdc
->
periph_clk
);
if
(
ret
)
return
;
if
((
status
&
ATMEL_HLCDC_PWM
)
==
0
)
break
;
usleep_range
(
1
,
10
);
return
ret
;
}
}
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
,
.
owner
=
THIS_MODULE
,
};
static
const
struct
atmel_hlcdc_pwm_errata
atmel_hlcdc_pwm_at91sam9x5_errata
=
{
.
slow_clk_erratum
=
true
,
};
return
atmel_hlcdc_pwm_apply
(
&
chip
->
chip
,
&
chip
->
chip
.
pwms
[
0
],
&
state
);
}
#endif
static
const
struct
atmel_hlcdc_pwm_errata
atmel_hlcdc_pwm_sama5d3_errata
=
{
.
div1_clk_erratum
=
true
,
};
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
[]
=
{
{
...
...
@@ -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
浏览文件 @
97512cea
...
...
@@ -58,17 +58,22 @@
#define PWM_MAX_PRD 0xFFFF
#define PRD_MAX_PRES 10
struct
atmel_pwm_registers
{
u8
period
;
u8
period_upd
;
u8
duty
;
u8
duty_upd
;
};
struct
atmel_pwm_chip
{
struct
pwm_chip
chip
;
struct
clk
*
clk
;
void
__iomem
*
base
;
const
struct
atmel_pwm_registers
*
regs
;
unsigned
int
updated_pwms
;
/* ISR is cleared when read, ensure only one thread does that */
struct
mutex
isr_lock
;
void
(
*
config
)(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
unsigned
long
dty
,
unsigned
long
prd
);
};
static
inline
struct
atmel_pwm_chip
*
to_atmel_pwm_chip
(
struct
pwm_chip
*
chip
)
...
...
@@ -105,153 +110,71 @@ static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip,
writel_relaxed
(
val
,
chip
->
base
+
base
+
offset
);
}
static
int
atmel_pwm_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
static
int
atmel_pwm_calculate_cprd_and_pres
(
struct
pwm_chip
*
chip
,
const
struct
pwm_state
*
state
,
unsigned
long
*
cprd
,
u32
*
pres
)
{
struct
atmel_pwm_chip
*
atmel_pwm
=
to_atmel_pwm_chip
(
chip
);
unsigned
long
prd
,
dty
;
unsigned
long
long
div
;
unsigned
int
pres
=
0
;
u32
val
;
int
ret
;
if
(
pwm_is_enabled
(
pwm
)
&&
(
period_ns
!=
pwm_get_period
(
pwm
)))
{
dev_err
(
chip
->
dev
,
"cannot change PWM period while enabled
\n
"
);
return
-
EBUSY
;
}
unsigned
long
long
cycles
=
state
->
period
;
/* Calculate the period cycles and prescale value */
div
=
(
unsigned
long
long
)
clk_get_rate
(
atmel_pwm
->
clk
)
*
period_ns
;
do_div
(
div
,
NSEC_PER_SEC
);
cycles
*=
clk_get_rate
(
atmel_pwm
->
clk
)
;
do_div
(
cycles
,
NSEC_PER_SEC
);
while
(
div
>
PWM_MAX_PRD
)
{
div
>>=
1
;
pres
++
;
}
for
(
*
pres
=
0
;
cycles
>
PWM_MAX_PRD
;
cycles
>>=
1
)
(
*
pres
)
++
;
if
(
pres
>
PRD_MAX_PRES
)
{
if
(
*
pres
>
PRD_MAX_PRES
)
{
dev_err
(
chip
->
dev
,
"pres exceeds the maximum value
\n
"
);
return
-
EINVAL
;
}
/* Calculate the duty cycles */
prd
=
div
;
div
*=
duty_ns
;
do_div
(
div
,
period_ns
);
dty
=
prd
-
div
;
ret
=
clk_enable
(
atmel_pwm
->
clk
);
if
(
ret
)
{
dev_err
(
chip
->
dev
,
"failed to enable PWM clock
\n
"
);
return
ret
;
}
/* It is necessary to preserve CPOL, inside CMR */
val
=
atmel_pwm_ch_readl
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
);
val
=
(
val
&
~
PWM_CMR_CPRE_MSK
)
|
(
pres
&
PWM_CMR_CPRE_MSK
);
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
,
val
);
atmel_pwm
->
config
(
chip
,
pwm
,
dty
,
prd
);
mutex_lock
(
&
atmel_pwm
->
isr_lock
);
atmel_pwm
->
updated_pwms
|=
atmel_pwm_readl
(
atmel_pwm
,
PWM_ISR
);
atmel_pwm
->
updated_pwms
&=
~
(
1
<<
pwm
->
hwpwm
);
mutex_unlock
(
&
atmel_pwm
->
isr_lock
);
clk_disable
(
atmel_pwm
->
clk
);
return
ret
;
}
static
void
atmel_pwm_config_v1
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
unsigned
long
dty
,
unsigned
long
prd
)
{
struct
atmel_pwm_chip
*
atmel_pwm
=
to_atmel_pwm_chip
(
chip
);
unsigned
int
val
;
*
cprd
=
cycles
;
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWMV1_CUPD
,
dty
);
val
=
atmel_pwm_ch_readl
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
);
val
&=
~
PWM_CMR_UPD_CDTY
;
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
,
val
);
/*
* If the PWM channel is enabled, only update CDTY by using the update
* register, it needs to set bit 10 of CMR to 0
*/
if
(
pwm_is_enabled
(
pwm
))
return
;
/*
* If the PWM channel is disabled, write value to duty and period
* registers directly.
*/
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWMV1_CDTY
,
dty
);
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWMV1_CPRD
,
prd
);
return
0
;
}
static
void
atmel_pwm_c
onfig_v2
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
unsigned
long
dty
,
unsigned
long
prd
)
static
void
atmel_pwm_c
alculate_cdty
(
const
struct
pwm_state
*
state
,
unsigned
long
cprd
,
unsigned
long
*
cdty
)
{
struct
atmel_pwm_chip
*
atmel_pwm
=
to_atmel_pwm_chip
(
chip
)
;
unsigned
long
long
cycles
=
state
->
duty_cycle
;
if
(
pwm_is_enabled
(
pwm
))
{
/*
* If the PWM channel is enabled, using the duty update register
* to update the value.
*/
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWMV2_CDTYUPD
,
dty
);
}
else
{
/*
* If the PWM channel is disabled, write value to duty and
* period registers directly.
*/
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWMV2_CDTY
,
dty
);
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWMV2_CPRD
,
prd
);
}
cycles
*=
cprd
;
do_div
(
cycles
,
state
->
period
);
*
cdty
=
cprd
-
cycles
;
}
static
int
atmel_pwm_set_polari
ty
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
enum
pwm_polarity
polari
ty
)
static
void
atmel_pwm_update_cd
ty
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
unsigned
long
cd
ty
)
{
struct
atmel_pwm_chip
*
atmel_pwm
=
to_atmel_pwm_chip
(
chip
);
u32
val
;
int
ret
;
val
=
atmel_pwm_ch_readl
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
);
if
(
polarity
==
PWM_POLARITY_NORMAL
)
val
&=
~
PWM_CMR_CPOL
;
else
val
|=
PWM_CMR_CPOL
;
ret
=
clk_enable
(
atmel_pwm
->
clk
);
if
(
ret
)
{
dev_err
(
chip
->
dev
,
"failed to enable PWM clock
\n
"
);
return
ret
;
if
(
atmel_pwm
->
regs
->
duty_upd
==
atmel_pwm
->
regs
->
period_upd
)
{
val
=
atmel_pwm_ch_readl
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
);
val
&=
~
PWM_CMR_UPD_CDTY
;
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
,
val
);
}
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
,
val
);
clk_disable
(
atmel_pwm
->
clk
);
return
0
;
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
atmel_pwm
->
regs
->
duty_upd
,
cdty
);
}
static
int
atmel_pwm_enable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
static
void
atmel_pwm_set_cprd_cdty
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
unsigned
long
cprd
,
unsigned
long
cdty
)
{
struct
atmel_pwm_chip
*
atmel_pwm
=
to_atmel_pwm_chip
(
chip
);
int
ret
;
ret
=
clk_enable
(
atmel_pwm
->
clk
);
if
(
ret
)
{
dev_err
(
chip
->
dev
,
"failed to enable PWM clock
\n
"
);
return
ret
;
}
atmel_pwm_writel
(
atmel_pwm
,
PWM_ENA
,
1
<<
pwm
->
hwpwm
);
return
0
;
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
atmel_pwm
->
regs
->
duty
,
cdty
);
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
atmel_pwm
->
regs
->
period
,
cprd
);
}
static
void
atmel_pwm_disable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
static
void
atmel_pwm_disable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
bool
disable_clk
)
{
struct
atmel_pwm_chip
*
atmel_pwm
=
to_atmel_pwm_chip
(
chip
);
unsigned
long
timeout
=
jiffies
+
2
*
HZ
;
...
...
@@ -282,37 +205,99 @@ static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
time_before
(
jiffies
,
timeout
))
usleep_range
(
10
,
100
);
clk_disable
(
atmel_pwm
->
clk
);
if
(
disable_clk
)
clk_disable
(
atmel_pwm
->
clk
);
}
static
int
atmel_pwm_apply
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
atmel_pwm_chip
*
atmel_pwm
=
to_atmel_pwm_chip
(
chip
);
struct
pwm_state
cstate
;
unsigned
long
cprd
,
cdty
;
u32
pres
,
val
;
int
ret
;
pwm_get_state
(
pwm
,
&
cstate
);
if
(
state
->
enabled
)
{
if
(
cstate
.
enabled
&&
cstate
.
polarity
==
state
->
polarity
&&
cstate
.
period
==
state
->
period
)
{
cprd
=
atmel_pwm_ch_readl
(
atmel_pwm
,
pwm
->
hwpwm
,
atmel_pwm
->
regs
->
period
);
atmel_pwm_calculate_cdty
(
state
,
cprd
,
&
cdty
);
atmel_pwm_update_cdty
(
chip
,
pwm
,
cdty
);
return
0
;
}
ret
=
atmel_pwm_calculate_cprd_and_pres
(
chip
,
state
,
&
cprd
,
&
pres
);
if
(
ret
)
{
dev_err
(
chip
->
dev
,
"failed to calculate cprd and prescaler
\n
"
);
return
ret
;
}
atmel_pwm_calculate_cdty
(
state
,
cprd
,
&
cdty
);
if
(
cstate
.
enabled
)
{
atmel_pwm_disable
(
chip
,
pwm
,
false
);
}
else
{
ret
=
clk_enable
(
atmel_pwm
->
clk
);
if
(
ret
)
{
dev_err
(
chip
->
dev
,
"failed to enable clock
\n
"
);
return
ret
;
}
}
/* It is necessary to preserve CPOL, inside CMR */
val
=
atmel_pwm_ch_readl
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
);
val
=
(
val
&
~
PWM_CMR_CPRE_MSK
)
|
(
pres
&
PWM_CMR_CPRE_MSK
);
if
(
state
->
polarity
==
PWM_POLARITY_NORMAL
)
val
&=
~
PWM_CMR_CPOL
;
else
val
|=
PWM_CMR_CPOL
;
atmel_pwm_ch_writel
(
atmel_pwm
,
pwm
->
hwpwm
,
PWM_CMR
,
val
);
atmel_pwm_set_cprd_cdty
(
chip
,
pwm
,
cprd
,
cdty
);
mutex_lock
(
&
atmel_pwm
->
isr_lock
);
atmel_pwm
->
updated_pwms
|=
atmel_pwm_readl
(
atmel_pwm
,
PWM_ISR
);
atmel_pwm
->
updated_pwms
&=
~
(
1
<<
pwm
->
hwpwm
);
mutex_unlock
(
&
atmel_pwm
->
isr_lock
);
atmel_pwm_writel
(
atmel_pwm
,
PWM_ENA
,
1
<<
pwm
->
hwpwm
);
}
else
if
(
cstate
.
enabled
)
{
atmel_pwm_disable
(
chip
,
pwm
,
true
);
}
return
0
;
}
static
const
struct
pwm_ops
atmel_pwm_ops
=
{
.
config
=
atmel_pwm_config
,
.
set_polarity
=
atmel_pwm_set_polarity
,
.
enable
=
atmel_pwm_enable
,
.
disable
=
atmel_pwm_disable
,
.
apply
=
atmel_pwm_apply
,
.
owner
=
THIS_MODULE
,
};
struct
atmel_pwm_data
{
void
(
*
config
)(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
unsigned
long
dty
,
unsigned
long
prd
);
static
const
struct
atmel_pwm_registers
atmel_pwm_regs_v1
=
{
.
period
=
PWMV1_CPRD
,
.
period_upd
=
PWMV1_CUPD
,
.
duty
=
PWMV1_CDTY
,
.
duty_upd
=
PWMV1_CUPD
,
};
static
const
struct
atmel_pwm_data
atmel_pwm_data_v1
=
{
.
config
=
atmel_pwm_config_v1
,
};
static
const
struct
atmel_pwm_data
atmel_pwm_data_v2
=
{
.
config
=
atmel_pwm_config_v2
,
static
const
struct
atmel_pwm_registers
atmel_pwm_regs_v2
=
{
.
period
=
PWMV2_CPRD
,
.
period_upd
=
PWMV2_CPRDUPD
,
.
duty
=
PWMV2_CDTY
,
.
duty_upd
=
PWMV2_CDTYUPD
,
};
static
const
struct
platform_device_id
atmel_pwm_devtypes
[]
=
{
{
.
name
=
"at91sam9rl-pwm"
,
.
driver_data
=
(
kernel_ulong_t
)
&
atmel_pwm_
data
_v1
,
.
driver_data
=
(
kernel_ulong_t
)
&
atmel_pwm_
regs
_v1
,
},
{
.
name
=
"sama5d3-pwm"
,
.
driver_data
=
(
kernel_ulong_t
)
&
atmel_pwm_
data
_v2
,
.
driver_data
=
(
kernel_ulong_t
)
&
atmel_pwm_
regs
_v2
,
},
{
/* sentinel */
},
...
...
@@ -322,17 +307,20 @@ MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes);
static
const
struct
of_device_id
atmel_pwm_dt_ids
[]
=
{
{
.
compatible
=
"atmel,at91sam9rl-pwm"
,
.
data
=
&
atmel_pwm_
data
_v1
,
.
data
=
&
atmel_pwm_
regs
_v1
,
},
{
.
compatible
=
"atmel,sama5d3-pwm"
,
.
data
=
&
atmel_pwm_data_v2
,
.
data
=
&
atmel_pwm_regs_v2
,
},
{
.
compatible
=
"atmel,sama5d2-pwm"
,
.
data
=
&
atmel_pwm_regs_v2
,
},
{
/* sentinel */
},
};
MODULE_DEVICE_TABLE
(
of
,
atmel_pwm_dt_ids
);
static
inline
const
struct
atmel_pwm_
data
*
static
inline
const
struct
atmel_pwm_
registers
*
atmel_pwm_get_driver_data
(
struct
platform_device
*
pdev
)
{
const
struct
platform_device_id
*
id
;
...
...
@@ -342,18 +330,18 @@ atmel_pwm_get_driver_data(struct platform_device *pdev)
id
=
platform_get_device_id
(
pdev
);
return
(
struct
atmel_pwm_
data
*
)
id
->
driver_data
;
return
(
struct
atmel_pwm_
registers
*
)
id
->
driver_data
;
}
static
int
atmel_pwm_probe
(
struct
platform_device
*
pdev
)
{
const
struct
atmel_pwm_
data
*
data
;
const
struct
atmel_pwm_
registers
*
regs
;
struct
atmel_pwm_chip
*
atmel_pwm
;
struct
resource
*
res
;
int
ret
;
data
=
atmel_pwm_get_driver_data
(
pdev
);
if
(
!
data
)
regs
=
atmel_pwm_get_driver_data
(
pdev
);
if
(
!
regs
)
return
-
ENODEV
;
atmel_pwm
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
atmel_pwm
),
GFP_KERNEL
);
...
...
@@ -385,7 +373,7 @@ static int atmel_pwm_probe(struct platform_device *pdev)
atmel_pwm
->
chip
.
base
=
-
1
;
atmel_pwm
->
chip
.
npwm
=
4
;
atmel_pwm
->
config
=
data
->
config
;
atmel_pwm
->
regs
=
regs
;
atmel_pwm
->
updated_pwms
=
0
;
mutex_init
(
&
atmel_pwm
->
isr_lock
);
...
...
drivers/pwm/pwm-mediatek.c
0 → 100644
浏览文件 @
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
浏览文件 @
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,22 +500,54 @@ 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
ret
;
return
0
;
}
static
int
pca9685_pwm_remove
(
struct
i2c_client
*
client
)
{
struct
pca9685
*
pca
=
i2c_get_clientdata
(
client
);
int
ret
;
regmap_update_bits
(
pca
->
regmap
,
PCA9685_MODE1
,
MODE1_SLEEP
,
MODE1_SLEEP
);
ret
=
pwmchip_remove
(
&
pca
->
chip
);
if
(
ret
)
return
ret
;
pm_runtime_disable
(
&
client
->
dev
);
return
0
;
}
return
pwmchip_remove
(
&
pca
->
chip
);
#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
;
}
static
int
pca9685_pwm_runtime_resume
(
struct
device
*
dev
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
pca9685
*
pca
=
i2c_get_clientdata
(
client
);
pca9685_set_sleep_mode
(
pca
,
0
);
return
0
;
}
#endif
static
const
struct
i2c_device_id
pca9685_id
[]
=
{
{
"pca9685"
,
0
},
{
/* sentinel */
},
...
...
@@ -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
浏览文件 @
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
,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录