Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
dc8e6e1e
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看板
提交
dc8e6e1e
编写于
9月 08, 2016
作者:
T
Thierry Reding
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'for-4.9/drivers' into for-next
上级
51f01e4c
2fbc487d
变更
16
隐藏空白更改
内联
并排
Showing
16 changed file
with
1158 addition
and
145 deletion
+1158
-145
Documentation/devicetree/bindings/pwm/pwm-meson.txt
Documentation/devicetree/bindings/pwm/pwm-meson.txt
+23
-0
Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
+2
-1
Documentation/devicetree/bindings/pwm/pwm-st.txt
Documentation/devicetree/bindings/pwm/pwm-st.txt
+5
-3
Documentation/devicetree/bindings/pwm/pwm-sun4i.txt
Documentation/devicetree/bindings/pwm/pwm-sun4i.txt
+1
-0
drivers/pwm/Kconfig
drivers/pwm/Kconfig
+9
-0
drivers/pwm/Makefile
drivers/pwm/Makefile
+1
-0
drivers/pwm/pwm-berlin.c
drivers/pwm/pwm-berlin.c
+84
-0
drivers/pwm/pwm-cros-ec.c
drivers/pwm/pwm-cros-ec.c
+2
-2
drivers/pwm/pwm-lpc18xx-sct.c
drivers/pwm/pwm-lpc18xx-sct.c
+8
-4
drivers/pwm/pwm-meson.c
drivers/pwm/pwm-meson.c
+529
-0
drivers/pwm/pwm-mtk-disp.c
drivers/pwm/pwm-mtk-disp.c
+72
-15
drivers/pwm/pwm-samsung.c
drivers/pwm/pwm-samsung.c
+12
-3
drivers/pwm/pwm-sti.c
drivers/pwm/pwm-sti.c
+385
-98
drivers/pwm/pwm-sun4i.c
drivers/pwm/pwm-sun4i.c
+9
-0
drivers/pwm/pwm-tipwmss.c
drivers/pwm/pwm-tipwmss.c
+0
-19
drivers/pwm/pwm-twl.c
drivers/pwm/pwm-twl.c
+16
-0
未找到文件。
Documentation/devicetree/bindings/pwm/pwm-meson.txt
0 → 100644
浏览文件 @
dc8e6e1e
Amlogic Meson PWM Controller
============================
Required properties:
- compatible: Shall contain "amlogic,meson8b-pwm" or "amlogic,meson-gxbb-pwm".
- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
the cells format.
Optional properties:
- clocks: Could contain one or two parents clocks phandle for each of the two
PWM channels.
- clock-names: Could contain at least the "clkin0" and/or "clkin1" names.
Example:
pwm_ab: pwm@8550 {
compatible = "amlogic,meson-gxbb-pwm";
reg = <0x0 0x08550 0x0 0x10>;
#pwm-cells = <3>;
status = "disabled";
clocks = <&xtal>, <&xtal>;
clock-names = "clkin0", "clkin1";
}
Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
浏览文件 @
dc8e6e1e
...
...
@@ -2,8 +2,9 @@ MediaTek display PWM controller
Required properties:
- compatible: should be "mediatek,<name>-disp-pwm":
- "mediatek,mt
8173-disp-pwm": found on mt8173
SoC.
- "mediatek,mt
2701-disp-pwm": found on mt2701
SoC.
- "mediatek,mt6595-disp-pwm": found on mt6595 SoC.
- "mediatek,mt8173-disp-pwm": found on mt8173 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.
...
...
Documentation/devicetree/bindings/pwm/pwm-st.txt
浏览文件 @
dc8e6e1e
...
...
@@ -13,13 +13,14 @@ Required parameters:
- pinctrl-0: List of phandles pointing to pin configuration nodes
for PWM module.
For Pinctrl properties, please refer to [1].
- clock-names:
Set to "pwm
".
- clock-names:
Valid entries are "pwm" and/or "capture
".
- clocks: phandle of the clock used by the PWM module.
For Clk properties, please refer to [2].
- interrupts: IRQ for the Capture device
Optional properties:
- st,pwm-num-chan: Number of available
channels. If not passed, the driver
will consider single channel by default
.
- st,pwm-num-chan: Number of available
PWM channels. Default is 0.
- st,capture-num-chan: Number of available Capture channels. Default is 0
.
[1] Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
[2] Documentation/devicetree/bindings/clock/clock-bindings.txt
...
...
@@ -38,4 +39,5 @@ pwm1: pwm@fe510000 {
clocks = <&clk_sysin>;
clock-names = "pwm";
st,pwm-num-chan = <4>;
st,capture-num-chan = <2>;
};
Documentation/devicetree/bindings/pwm/pwm-sun4i.txt
浏览文件 @
dc8e6e1e
...
...
@@ -6,6 +6,7 @@ Required properties:
- "allwinner,sun5i-a10s-pwm"
- "allwinner,sun5i-a13-pwm"
- "allwinner,sun7i-a20-pwm"
- "allwinner,sun8i-h3-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.
...
...
drivers/pwm/Kconfig
浏览文件 @
dc8e6e1e
...
...
@@ -262,6 +262,15 @@ config PWM_LPSS_PLATFORM
To compile this driver as a module, choose M here: the module
will be called pwm-lpss-platform.
config PWM_MESON
tristate "Amlogic Meson PWM driver"
depends on ARCH_MESON
help
The platform driver for Amlogic Meson PWM controller.
To compile this driver as a module, choose M here: the module
will be called pwm-meson.
config PWM_MTK_DISP
tristate "MediaTek display PWM driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
...
...
drivers/pwm/Makefile
浏览文件 @
dc8e6e1e
...
...
@@ -24,6 +24,7 @@ obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
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_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-berlin.c
浏览文件 @
dc8e6e1e
...
...
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#define BERLIN_PWM_EN 0x0
#define BERLIN_PWM_ENABLE BIT(0)
...
...
@@ -27,6 +28,13 @@
#define BERLIN_PWM_TCNT 0xc
#define BERLIN_PWM_MAX_TCNT 65535
struct
berlin_pwm_channel
{
u32
enable
;
u32
ctrl
;
u32
duty
;
u32
tcnt
;
};
struct
berlin_pwm_chip
{
struct
pwm_chip
chip
;
struct
clk
*
clk
;
...
...
@@ -55,6 +63,25 @@ static inline void berlin_pwm_writel(struct berlin_pwm_chip *chip,
writel_relaxed
(
value
,
chip
->
base
+
channel
*
0x10
+
offset
);
}
static
int
berlin_pwm_request
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
berlin_pwm_channel
*
channel
;
channel
=
kzalloc
(
sizeof
(
*
channel
),
GFP_KERNEL
);
if
(
!
channel
)
return
-
ENOMEM
;
return
pwm_set_chip_data
(
pwm
,
channel
);
}
static
void
berlin_pwm_free
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
berlin_pwm_channel
*
channel
=
pwm_get_chip_data
(
pwm
);
pwm_set_chip_data
(
pwm
,
NULL
);
kfree
(
channel
);
}
static
int
berlin_pwm_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm_dev
,
int
duty_ns
,
int
period_ns
)
{
...
...
@@ -137,6 +164,8 @@ static void berlin_pwm_disable(struct pwm_chip *chip,
}
static
const
struct
pwm_ops
berlin_pwm_ops
=
{
.
request
=
berlin_pwm_request
,
.
free
=
berlin_pwm_free
,
.
config
=
berlin_pwm_config
,
.
set_polarity
=
berlin_pwm_set_polarity
,
.
enable
=
berlin_pwm_enable
,
...
...
@@ -204,12 +233,67 @@ static int berlin_pwm_remove(struct platform_device *pdev)
return
ret
;
}
#ifdef CONFIG_PM_SLEEP
static
int
berlin_pwm_suspend
(
struct
device
*
dev
)
{
struct
berlin_pwm_chip
*
pwm
=
dev_get_drvdata
(
dev
);
unsigned
int
i
;
for
(
i
=
0
;
i
<
pwm
->
chip
.
npwm
;
i
++
)
{
struct
berlin_pwm_channel
*
channel
;
channel
=
pwm_get_chip_data
(
&
pwm
->
chip
.
pwms
[
i
]);
if
(
!
channel
)
continue
;
channel
->
enable
=
berlin_pwm_readl
(
pwm
,
i
,
BERLIN_PWM_ENABLE
);
channel
->
ctrl
=
berlin_pwm_readl
(
pwm
,
i
,
BERLIN_PWM_CONTROL
);
channel
->
duty
=
berlin_pwm_readl
(
pwm
,
i
,
BERLIN_PWM_DUTY
);
channel
->
tcnt
=
berlin_pwm_readl
(
pwm
,
i
,
BERLIN_PWM_TCNT
);
}
clk_disable_unprepare
(
pwm
->
clk
);
return
0
;
}
static
int
berlin_pwm_resume
(
struct
device
*
dev
)
{
struct
berlin_pwm_chip
*
pwm
=
dev_get_drvdata
(
dev
);
unsigned
int
i
;
int
ret
;
ret
=
clk_prepare_enable
(
pwm
->
clk
);
if
(
ret
)
return
ret
;
for
(
i
=
0
;
i
<
pwm
->
chip
.
npwm
;
i
++
)
{
struct
berlin_pwm_channel
*
channel
;
channel
=
pwm_get_chip_data
(
&
pwm
->
chip
.
pwms
[
i
]);
if
(
!
channel
)
continue
;
berlin_pwm_writel
(
pwm
,
i
,
channel
->
ctrl
,
BERLIN_PWM_CONTROL
);
berlin_pwm_writel
(
pwm
,
i
,
channel
->
duty
,
BERLIN_PWM_DUTY
);
berlin_pwm_writel
(
pwm
,
i
,
channel
->
tcnt
,
BERLIN_PWM_TCNT
);
berlin_pwm_writel
(
pwm
,
i
,
channel
->
enable
,
BERLIN_PWM_ENABLE
);
}
return
0
;
}
#endif
static
SIMPLE_DEV_PM_OPS
(
berlin_pwm_pm_ops
,
berlin_pwm_suspend
,
berlin_pwm_resume
);
static
struct
platform_driver
berlin_pwm_driver
=
{
.
probe
=
berlin_pwm_probe
,
.
remove
=
berlin_pwm_remove
,
.
driver
=
{
.
name
=
"berlin-pwm"
,
.
of_match_table
=
berlin_pwm_match
,
.
pm
=
&
berlin_pwm_pm_ops
,
},
};
module_platform_driver
(
berlin_pwm_driver
);
...
...
drivers/pwm/pwm-cros-ec.c
浏览文件 @
dc8e6e1e
...
...
@@ -38,7 +38,7 @@ static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty)
struct
{
struct
cros_ec_command
msg
;
struct
ec_params_pwm_set_duty
params
;
}
buf
;
}
__packed
buf
;
struct
ec_params_pwm_set_duty
*
params
=
&
buf
.
params
;
struct
cros_ec_command
*
msg
=
&
buf
.
msg
;
...
...
@@ -65,7 +65,7 @@ static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index,
struct
ec_params_pwm_get_duty
params
;
struct
ec_response_pwm_get_duty
resp
;
};
}
buf
;
}
__packed
buf
;
struct
ec_params_pwm_get_duty
*
params
=
&
buf
.
params
;
struct
ec_response_pwm_get_duty
*
resp
=
&
buf
.
resp
;
struct
cros_ec_command
*
msg
=
&
buf
.
msg
;
...
...
drivers/pwm/pwm-lpc18xx-sct.c
浏览文件 @
dc8e6e1e
...
...
@@ -413,14 +413,18 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev)
}
for
(
i
=
0
;
i
<
lpc18xx_pwm
->
chip
.
npwm
;
i
++
)
{
struct
lpc18xx_pwm_data
*
data
;
pwm
=
&
lpc18xx_pwm
->
chip
.
pwms
[
i
];
pwm
->
chip_data
=
devm_kzalloc
(
lpc18xx_pwm
->
dev
,
sizeof
(
struct
lpc18xx_pwm_
data
),
GFP_KERNEL
);
if
(
!
pwm
->
chip_
data
)
{
data
=
devm_kzalloc
(
lpc18xx_pwm
->
dev
,
sizeof
(
*
data
),
GFP_KERNEL
);
if
(
!
data
)
{
ret
=
-
ENOMEM
;
goto
remove_pwmchip
;
}
pwm_set_chip_data
(
pwm
,
data
);
}
platform_set_drvdata
(
pdev
,
lpc18xx_pwm
);
...
...
drivers/pwm/pwm-meson.c
0 → 100644
浏览文件 @
dc8e6e1e
/*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright (c) 2016 BayLibre, SAS.
* Author: Neil Armstrong <narmstrong@baylibre.com>
* Copyright (C) 2014 Amlogic, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* BSD LICENSE
*
* Copyright (c) 2016 BayLibre, SAS.
* Author: Neil Armstrong <narmstrong@baylibre.com>
* Copyright (C) 2014 Amlogic, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#define REG_PWM_A 0x0
#define REG_PWM_B 0x4
#define PWM_HIGH_SHIFT 16
#define REG_MISC_AB 0x8
#define MISC_B_CLK_EN BIT(23)
#define MISC_A_CLK_EN BIT(15)
#define MISC_CLK_DIV_MASK 0x7f
#define MISC_B_CLK_DIV_SHIFT 16
#define MISC_A_CLK_DIV_SHIFT 8
#define MISC_B_CLK_SEL_SHIFT 6
#define MISC_A_CLK_SEL_SHIFT 4
#define MISC_CLK_SEL_WIDTH 2
#define MISC_B_EN BIT(1)
#define MISC_A_EN BIT(0)
static
const
unsigned
int
mux_reg_shifts
[]
=
{
MISC_A_CLK_SEL_SHIFT
,
MISC_B_CLK_SEL_SHIFT
};
struct
meson_pwm_channel
{
unsigned
int
hi
;
unsigned
int
lo
;
u8
pre_div
;
struct
pwm_state
state
;
struct
clk
*
clk_parent
;
struct
clk_mux
mux
;
struct
clk
*
clk
;
};
struct
meson_pwm_data
{
const
char
*
const
*
parent_names
;
};
struct
meson_pwm
{
struct
pwm_chip
chip
;
const
struct
meson_pwm_data
*
data
;
void
__iomem
*
base
;
u8
inverter_mask
;
spinlock_t
lock
;
};
static
inline
struct
meson_pwm
*
to_meson_pwm
(
struct
pwm_chip
*
chip
)
{
return
container_of
(
chip
,
struct
meson_pwm
,
chip
);
}
static
int
meson_pwm_request
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
meson_pwm_channel
*
channel
=
pwm_get_chip_data
(
pwm
);
struct
device
*
dev
=
chip
->
dev
;
int
err
;
if
(
!
channel
)
return
-
ENODEV
;
if
(
channel
->
clk_parent
)
{
err
=
clk_set_parent
(
channel
->
clk
,
channel
->
clk_parent
);
if
(
err
<
0
)
{
dev_err
(
dev
,
"failed to set parent %s for %s: %d
\n
"
,
__clk_get_name
(
channel
->
clk_parent
),
__clk_get_name
(
channel
->
clk
),
err
);
return
err
;
}
}
err
=
clk_prepare_enable
(
channel
->
clk
);
if
(
err
<
0
)
{
dev_err
(
dev
,
"failed to enable clock %s: %d
\n
"
,
__clk_get_name
(
channel
->
clk
),
err
);
return
err
;
}
chip
->
ops
->
get_state
(
chip
,
pwm
,
&
channel
->
state
);
return
0
;
}
static
void
meson_pwm_free
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
meson_pwm_channel
*
channel
=
pwm_get_chip_data
(
pwm
);
if
(
channel
)
clk_disable_unprepare
(
channel
->
clk
);
}
static
int
meson_pwm_calc
(
struct
meson_pwm
*
meson
,
struct
meson_pwm_channel
*
channel
,
unsigned
int
id
,
unsigned
int
duty
,
unsigned
int
period
)
{
unsigned
int
pre_div
,
cnt
,
duty_cnt
;
unsigned
long
fin_freq
=
-
1
,
fin_ns
;
if
(
~
(
meson
->
inverter_mask
>>
id
)
&
0x1
)
duty
=
period
-
duty
;
if
(
period
==
channel
->
state
.
period
&&
duty
==
channel
->
state
.
duty_cycle
)
return
0
;
fin_freq
=
clk_get_rate
(
channel
->
clk
);
if
(
fin_freq
==
0
)
{
dev_err
(
meson
->
chip
.
dev
,
"invalid source clock frequency
\n
"
);
return
-
EINVAL
;
}
dev_dbg
(
meson
->
chip
.
dev
,
"fin_freq: %lu Hz
\n
"
,
fin_freq
);
fin_ns
=
NSEC_PER_SEC
/
fin_freq
;
/* Calc pre_div with the period */
for
(
pre_div
=
0
;
pre_div
<
MISC_CLK_DIV_MASK
;
pre_div
++
)
{
cnt
=
DIV_ROUND_CLOSEST
(
period
,
fin_ns
*
(
pre_div
+
1
));
dev_dbg
(
meson
->
chip
.
dev
,
"fin_ns=%lu pre_div=%u cnt=%u
\n
"
,
fin_ns
,
pre_div
,
cnt
);
if
(
cnt
<=
0xffff
)
break
;
}
if
(
pre_div
==
MISC_CLK_DIV_MASK
)
{
dev_err
(
meson
->
chip
.
dev
,
"unable to get period pre_div
\n
"
);
return
-
EINVAL
;
}
dev_dbg
(
meson
->
chip
.
dev
,
"period=%u pre_div=%u cnt=%u
\n
"
,
period
,
pre_div
,
cnt
);
if
(
duty
==
period
)
{
channel
->
pre_div
=
pre_div
;
channel
->
hi
=
cnt
;
channel
->
lo
=
0
;
}
else
if
(
duty
==
0
)
{
channel
->
pre_div
=
pre_div
;
channel
->
hi
=
0
;
channel
->
lo
=
cnt
;
}
else
{
/* Then check is we can have the duty with the same pre_div */
duty_cnt
=
DIV_ROUND_CLOSEST
(
duty
,
fin_ns
*
(
pre_div
+
1
));
if
(
duty_cnt
>
0xffff
)
{
dev_err
(
meson
->
chip
.
dev
,
"unable to get duty cycle
\n
"
);
return
-
EINVAL
;
}
dev_dbg
(
meson
->
chip
.
dev
,
"duty=%u pre_div=%u duty_cnt=%u
\n
"
,
duty
,
pre_div
,
duty_cnt
);
channel
->
pre_div
=
pre_div
;
channel
->
hi
=
duty_cnt
;
channel
->
lo
=
cnt
-
duty_cnt
;
}
return
0
;
}
static
void
meson_pwm_enable
(
struct
meson_pwm
*
meson
,
struct
meson_pwm_channel
*
channel
,
unsigned
int
id
)
{
u32
value
,
clk_shift
,
clk_enable
,
enable
;
unsigned
int
offset
;
switch
(
id
)
{
case
0
:
clk_shift
=
MISC_A_CLK_DIV_SHIFT
;
clk_enable
=
MISC_A_CLK_EN
;
enable
=
MISC_A_EN
;
offset
=
REG_PWM_A
;
break
;
case
1
:
clk_shift
=
MISC_B_CLK_DIV_SHIFT
;
clk_enable
=
MISC_B_CLK_EN
;
enable
=
MISC_B_EN
;
offset
=
REG_PWM_B
;
break
;
default:
return
;
}
value
=
readl
(
meson
->
base
+
REG_MISC_AB
);
value
&=
~
(
MISC_CLK_DIV_MASK
<<
clk_shift
);
value
|=
channel
->
pre_div
<<
clk_shift
;
value
|=
clk_enable
;
writel
(
value
,
meson
->
base
+
REG_MISC_AB
);
value
=
(
channel
->
hi
<<
PWM_HIGH_SHIFT
)
|
channel
->
lo
;
writel
(
value
,
meson
->
base
+
offset
);
value
=
readl
(
meson
->
base
+
REG_MISC_AB
);
value
|=
enable
;
writel
(
value
,
meson
->
base
+
REG_MISC_AB
);
}
static
void
meson_pwm_disable
(
struct
meson_pwm
*
meson
,
unsigned
int
id
)
{
u32
value
,
enable
;
switch
(
id
)
{
case
0
:
enable
=
MISC_A_EN
;
break
;
case
1
:
enable
=
MISC_B_EN
;
break
;
default:
return
;
}
value
=
readl
(
meson
->
base
+
REG_MISC_AB
);
value
&=
~
enable
;
writel
(
value
,
meson
->
base
+
REG_MISC_AB
);
}
static
int
meson_pwm_apply
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
meson_pwm_channel
*
channel
=
pwm_get_chip_data
(
pwm
);
struct
meson_pwm
*
meson
=
to_meson_pwm
(
chip
);
unsigned
long
flags
;
int
err
=
0
;
if
(
!
state
)
return
-
EINVAL
;
spin_lock_irqsave
(
&
meson
->
lock
,
flags
);
if
(
!
state
->
enabled
)
{
meson_pwm_disable
(
meson
,
pwm
->
hwpwm
);
channel
->
state
.
enabled
=
false
;
goto
unlock
;
}
if
(
state
->
period
!=
channel
->
state
.
period
||
state
->
duty_cycle
!=
channel
->
state
.
duty_cycle
||
state
->
polarity
!=
channel
->
state
.
polarity
)
{
if
(
channel
->
state
.
enabled
)
{
meson_pwm_disable
(
meson
,
pwm
->
hwpwm
);
channel
->
state
.
enabled
=
false
;
}
if
(
state
->
polarity
!=
channel
->
state
.
polarity
)
{
if
(
state
->
polarity
==
PWM_POLARITY_NORMAL
)
meson
->
inverter_mask
|=
BIT
(
pwm
->
hwpwm
);
else
meson
->
inverter_mask
&=
~
BIT
(
pwm
->
hwpwm
);
}
err
=
meson_pwm_calc
(
meson
,
channel
,
pwm
->
hwpwm
,
state
->
duty_cycle
,
state
->
period
);
if
(
err
<
0
)
goto
unlock
;
channel
->
state
.
polarity
=
state
->
polarity
;
channel
->
state
.
period
=
state
->
period
;
channel
->
state
.
duty_cycle
=
state
->
duty_cycle
;
}
if
(
state
->
enabled
&&
!
channel
->
state
.
enabled
)
{
meson_pwm_enable
(
meson
,
channel
,
pwm
->
hwpwm
);
channel
->
state
.
enabled
=
true
;
}
unlock:
spin_unlock_irqrestore
(
&
meson
->
lock
,
flags
);
return
err
;
}
static
void
meson_pwm_get_state
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
meson_pwm
*
meson
=
to_meson_pwm
(
chip
);
u32
value
,
mask
;
if
(
!
state
)
return
;
switch
(
pwm
->
hwpwm
)
{
case
0
:
mask
=
MISC_A_EN
;
break
;
case
1
:
mask
=
MISC_B_EN
;
break
;
default:
return
;
}
value
=
readl
(
meson
->
base
+
REG_MISC_AB
);
state
->
enabled
=
(
value
&
mask
)
!=
0
;
}
static
const
struct
pwm_ops
meson_pwm_ops
=
{
.
request
=
meson_pwm_request
,
.
free
=
meson_pwm_free
,
.
apply
=
meson_pwm_apply
,
.
get_state
=
meson_pwm_get_state
,
.
owner
=
THIS_MODULE
,
};
static
const
char
*
const
pwm_meson8b_parent_names
[]
=
{
"xtal"
,
"vid_pll"
,
"fclk_div4"
,
"fclk_div3"
};
static
const
struct
meson_pwm_data
pwm_meson8b_data
=
{
.
parent_names
=
pwm_meson8b_parent_names
,
};
static
const
char
*
const
pwm_gxbb_parent_names
[]
=
{
"xtal"
,
"hdmi_pll"
,
"fclk_div4"
,
"fclk_div3"
};
static
const
struct
meson_pwm_data
pwm_gxbb_data
=
{
.
parent_names
=
pwm_gxbb_parent_names
,
};
static
const
struct
of_device_id
meson_pwm_matches
[]
=
{
{
.
compatible
=
"amlogic,meson8b-pwm"
,
.
data
=
&
pwm_meson8b_data
},
{
.
compatible
=
"amlogic,meson-gxbb-pwm"
,
.
data
=
&
pwm_gxbb_data
},
{},
};
MODULE_DEVICE_TABLE
(
of
,
meson_pwm_matches
);
static
int
meson_pwm_init_channels
(
struct
meson_pwm
*
meson
,
struct
meson_pwm_channel
*
channels
)
{
struct
device
*
dev
=
meson
->
chip
.
dev
;
struct
device_node
*
np
=
dev
->
of_node
;
struct
clk_init_data
init
;
unsigned
int
i
;
char
name
[
255
];
int
err
;
for
(
i
=
0
;
i
<
meson
->
chip
.
npwm
;
i
++
)
{
struct
meson_pwm_channel
*
channel
=
&
channels
[
i
];
snprintf
(
name
,
sizeof
(
name
),
"%s#mux%u"
,
np
->
full_name
,
i
);
init
.
name
=
name
;
init
.
ops
=
&
clk_mux_ops
;
init
.
flags
=
CLK_IS_BASIC
;
init
.
parent_names
=
meson
->
data
->
parent_names
;
init
.
num_parents
=
1
<<
MISC_CLK_SEL_WIDTH
;
channel
->
mux
.
reg
=
meson
->
base
+
REG_MISC_AB
;
channel
->
mux
.
shift
=
mux_reg_shifts
[
i
];
channel
->
mux
.
mask
=
BIT
(
MISC_CLK_SEL_WIDTH
)
-
1
;
channel
->
mux
.
flags
=
0
;
channel
->
mux
.
lock
=
&
meson
->
lock
;
channel
->
mux
.
table
=
NULL
;
channel
->
mux
.
hw
.
init
=
&
init
;
channel
->
clk
=
devm_clk_register
(
dev
,
&
channel
->
mux
.
hw
);
if
(
IS_ERR
(
channel
->
clk
))
{
err
=
PTR_ERR
(
channel
->
clk
);
dev_err
(
dev
,
"failed to register %s: %d
\n
"
,
name
,
err
);
return
err
;
}
snprintf
(
name
,
sizeof
(
name
),
"clkin%u"
,
i
);
channel
->
clk_parent
=
devm_clk_get
(
dev
,
name
);
if
(
IS_ERR
(
channel
->
clk_parent
))
{
err
=
PTR_ERR
(
channel
->
clk_parent
);
if
(
err
==
-
EPROBE_DEFER
)
return
err
;
channel
->
clk_parent
=
NULL
;
}
}
return
0
;
}
static
void
meson_pwm_add_channels
(
struct
meson_pwm
*
meson
,
struct
meson_pwm_channel
*
channels
)
{
unsigned
int
i
;
for
(
i
=
0
;
i
<
meson
->
chip
.
npwm
;
i
++
)
pwm_set_chip_data
(
&
meson
->
chip
.
pwms
[
i
],
&
channels
[
i
]);
}
static
int
meson_pwm_probe
(
struct
platform_device
*
pdev
)
{
struct
meson_pwm_channel
*
channels
;
struct
meson_pwm
*
meson
;
struct
resource
*
regs
;
int
err
;
meson
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
meson
),
GFP_KERNEL
);
if
(
!
meson
)
return
-
ENOMEM
;
regs
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
meson
->
base
=
devm_ioremap_resource
(
&
pdev
->
dev
,
regs
);
if
(
IS_ERR
(
meson
->
base
))
return
PTR_ERR
(
meson
->
base
);
meson
->
chip
.
dev
=
&
pdev
->
dev
;
meson
->
chip
.
ops
=
&
meson_pwm_ops
;
meson
->
chip
.
base
=
-
1
;
meson
->
chip
.
npwm
=
2
;
meson
->
chip
.
of_xlate
=
of_pwm_xlate_with_flags
;
meson
->
chip
.
of_pwm_n_cells
=
3
;
meson
->
data
=
of_device_get_match_data
(
&
pdev
->
dev
);
meson
->
inverter_mask
=
BIT
(
meson
->
chip
.
npwm
)
-
1
;
channels
=
devm_kcalloc
(
&
pdev
->
dev
,
meson
->
chip
.
npwm
,
sizeof
(
*
meson
),
GFP_KERNEL
);
if
(
!
channels
)
return
-
ENOMEM
;
err
=
meson_pwm_init_channels
(
meson
,
channels
);
if
(
err
<
0
)
return
err
;
err
=
pwmchip_add
(
&
meson
->
chip
);
if
(
err
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"failed to register PWM chip: %d
\n
"
,
err
);
return
err
;
}
meson_pwm_add_channels
(
meson
,
channels
);
platform_set_drvdata
(
pdev
,
meson
);
return
0
;
}
static
int
meson_pwm_remove
(
struct
platform_device
*
pdev
)
{
struct
meson_pwm
*
meson
=
platform_get_drvdata
(
pdev
);
return
pwmchip_remove
(
&
meson
->
chip
);
}
static
struct
platform_driver
meson_pwm_driver
=
{
.
driver
=
{
.
name
=
"meson-pwm"
,
.
of_match_table
=
meson_pwm_matches
,
},
.
probe
=
meson_pwm_probe
,
.
remove
=
meson_pwm_remove
,
};
module_platform_driver
(
meson_pwm_driver
);
MODULE_ALIAS
(
"platform:meson-pwm"
);
MODULE_DESCRIPTION
(
"Amlogic Meson PWM Generator driver"
);
MODULE_AUTHOR
(
"Neil Armstrong <narmstrong@baylibre.com>"
);
MODULE_LICENSE
(
"Dual BSD/GPL"
);
drivers/pwm/pwm-mtk-disp.c
浏览文件 @
dc8e6e1e
...
...
@@ -18,30 +18,40 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#define DISP_PWM_EN 0x00
#define PWM_ENABLE_MASK BIT(0)
#define DISP_PWM_COMMIT 0x08
#define PWM_COMMIT_MASK BIT(0)
#define DISP_PWM_CON_0 0x10
#define PWM_CLKDIV_SHIFT 16
#define PWM_CLKDIV_MAX 0x3ff
#define PWM_CLKDIV_MASK (PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)
#define DISP_PWM_CON_1 0x14
#define PWM_PERIOD_BIT_WIDTH 12
#define PWM_PERIOD_MASK ((1 << PWM_PERIOD_BIT_WIDTH) - 1)
#define PWM_HIGH_WIDTH_SHIFT 16
#define PWM_HIGH_WIDTH_MASK (0x1fff << PWM_HIGH_WIDTH_SHIFT)
struct
mtk_pwm_data
{
u32
enable_mask
;
unsigned
int
con0
;
u32
con0_sel
;
unsigned
int
con1
;
bool
has_commit
;
unsigned
int
commit
;
unsigned
int
commit_mask
;
unsigned
int
bls_debug
;
u32
bls_debug_mask
;
};
struct
mtk_disp_pwm
{
struct
pwm_chip
chip
;
const
struct
mtk_pwm_data
*
data
;
struct
clk
*
clk_main
;
struct
clk
*
clk_mm
;
void
__iomem
*
base
;
...
...
@@ -106,12 +116,21 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return
err
;
}
mtk_disp_pwm_update_bits
(
mdp
,
DISP_PWM_CON_0
,
PWM_CLKDIV_MASK
,
mtk_disp_pwm_update_bits
(
mdp
,
mdp
->
data
->
con0
,
PWM_CLKDIV_MASK
,
clk_div
<<
PWM_CLKDIV_SHIFT
);
mtk_disp_pwm_update_bits
(
mdp
,
DISP_PWM_CON_1
,
PWM_PERIOD_MASK
|
PWM_HIGH_WIDTH_MASK
,
value
);
mtk_disp_pwm_update_bits
(
mdp
,
DISP_PWM_COMMIT
,
PWM_COMMIT_MASK
,
1
);
mtk_disp_pwm_update_bits
(
mdp
,
DISP_PWM_COMMIT
,
PWM_COMMIT_MASK
,
0
);
mtk_disp_pwm_update_bits
(
mdp
,
mdp
->
data
->
con1
,
PWM_PERIOD_MASK
|
PWM_HIGH_WIDTH_MASK
,
value
);
if
(
mdp
->
data
->
has_commit
)
{
mtk_disp_pwm_update_bits
(
mdp
,
mdp
->
data
->
commit
,
mdp
->
data
->
commit_mask
,
mdp
->
data
->
commit_mask
);
mtk_disp_pwm_update_bits
(
mdp
,
mdp
->
data
->
commit
,
mdp
->
data
->
commit_mask
,
0x0
);
}
clk_disable
(
mdp
->
clk_mm
);
clk_disable
(
mdp
->
clk_main
);
...
...
@@ -134,7 +153,8 @@ static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
return
err
;
}
mtk_disp_pwm_update_bits
(
mdp
,
DISP_PWM_EN
,
PWM_ENABLE_MASK
,
1
);
mtk_disp_pwm_update_bits
(
mdp
,
DISP_PWM_EN
,
mdp
->
data
->
enable_mask
,
mdp
->
data
->
enable_mask
);
return
0
;
}
...
...
@@ -143,7 +163,8 @@ static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct
mtk_disp_pwm
*
mdp
=
to_mtk_disp_pwm
(
chip
);
mtk_disp_pwm_update_bits
(
mdp
,
DISP_PWM_EN
,
PWM_ENABLE_MASK
,
0
);
mtk_disp_pwm_update_bits
(
mdp
,
DISP_PWM_EN
,
mdp
->
data
->
enable_mask
,
0x0
);
clk_disable
(
mdp
->
clk_mm
);
clk_disable
(
mdp
->
clk_main
);
...
...
@@ -166,6 +187,8 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
if
(
!
mdp
)
return
-
ENOMEM
;
mdp
->
data
=
of_device_get_match_data
(
&
pdev
->
dev
);
r
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
mdp
->
base
=
devm_ioremap_resource
(
&
pdev
->
dev
,
r
);
if
(
IS_ERR
(
mdp
->
base
))
...
...
@@ -200,6 +223,19 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
platform_set_drvdata
(
pdev
,
mdp
);
/*
* For MT2701, disable double buffer before writing register
* and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.
*/
if
(
!
mdp
->
data
->
has_commit
)
{
mtk_disp_pwm_update_bits
(
mdp
,
mdp
->
data
->
bls_debug
,
mdp
->
data
->
bls_debug_mask
,
mdp
->
data
->
bls_debug_mask
);
mtk_disp_pwm_update_bits
(
mdp
,
mdp
->
data
->
con0
,
mdp
->
data
->
con0_sel
,
mdp
->
data
->
con0_sel
);
}
return
0
;
disable_clk_mm:
...
...
@@ -221,9 +257,30 @@ static int mtk_disp_pwm_remove(struct platform_device *pdev)
return
ret
;
}
static
const
struct
mtk_pwm_data
mt2701_pwm_data
=
{
.
enable_mask
=
BIT
(
16
),
.
con0
=
0xa8
,
.
con0_sel
=
0x2
,
.
con1
=
0xac
,
.
has_commit
=
false
,
.
bls_debug
=
0xb0
,
.
bls_debug_mask
=
0x3
,
};
static
const
struct
mtk_pwm_data
mt8173_pwm_data
=
{
.
enable_mask
=
BIT
(
0
),
.
con0
=
0x10
,
.
con0_sel
=
0x0
,
.
con1
=
0x14
,
.
has_commit
=
true
,
.
commit
=
0x8
,
.
commit_mask
=
0x1
,
};
static
const
struct
of_device_id
mtk_disp_pwm_of_match
[]
=
{
{
.
compatible
=
"mediatek,mt8173-disp-pwm"
},
{
.
compatible
=
"mediatek,mt6595-disp-pwm"
},
{
.
compatible
=
"mediatek,mt2701-disp-pwm"
,
.
data
=
&
mt2701_pwm_data
},
{
.
compatible
=
"mediatek,mt6595-disp-pwm"
,
.
data
=
&
mt8173_pwm_data
},
{
.
compatible
=
"mediatek,mt8173-disp-pwm"
,
.
data
=
&
mt8173_pwm_data
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
mtk_disp_pwm_of_match
);
...
...
drivers/pwm/pwm-samsung.c
浏览文件 @
dc8e6e1e
...
...
@@ -193,9 +193,18 @@ static unsigned long pwm_samsung_calc_tin(struct samsung_pwm_chip *chip,
* divider settings and choose the lowest divisor that can generate
* frequencies lower than requested.
*/
for
(
div
=
variant
->
div_base
;
div
<
4
;
++
div
)
if
((
rate
>>
(
variant
->
bits
+
div
))
<
freq
)
break
;
if
(
variant
->
bits
<
32
)
{
/* Only for s3c24xx */
for
(
div
=
variant
->
div_base
;
div
<
4
;
++
div
)
if
((
rate
>>
(
variant
->
bits
+
div
))
<
freq
)
break
;
}
else
{
/*
* Other variants have enough counter bits to generate any
* requested rate, so no need to check higher divisors.
*/
div
=
variant
->
div_base
;
}
pwm_samsung_set_divisor
(
chip
,
chan
,
BIT
(
div
));
...
...
drivers/pwm/pwm-sti.c
浏览文件 @
dc8e6e1e
/*
* PWM device driver for ST SoCs.
* Author: Ajit Pal Singh <ajitpal.singh@st.com>
* PWM device driver for ST SoCs
*
* Copyright (C) 2013-2016 STMicroelectronics (R&D) Limited
*
* Copyright (C) 2013-2014 STMicroelectronics (R&D) Limited
* Author: Ajit Pal Singh <ajitpal.singh@st.com>
* Lee Jones <lee.jones@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
...
...
@@ -11,6 +13,7 @@
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/math64.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
...
...
@@ -18,43 +21,82 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
#define PWM_OUT_VAL(x) (0x00 + (4 * (x)))
/* Device's Duty Cycle register */
#define PWM_CPT_VAL(x) (0x10 + (4 * (x)))
/* Capture value */
#define PWM_CPT_EDGE(x) (0x30 + (4 * (x)))
/* Edge to capture on */
#define STI_DS_REG(ch) (4 * (ch))
/* Channel's Duty Cycle register */
#define STI_PWMCR 0x50
/* Control/Config register */
#define STI_INTEN 0x54
/* Interrupt Enable/Disable register */
#define PWM_PRESCALE_LOW_MASK 0x0f
#define PWM_PRESCALE_HIGH_MASK 0xf0
#define STI_PWM_CTRL 0x50
/* Control/Config register */
#define STI_INT_EN 0x54
/* Interrupt Enable/Disable register */
#define STI_INT_STA 0x58
/* Interrupt Status register */
#define PWM_INT_ACK 0x5c
#define PWM_PRESCALE_LOW_MASK 0x0f
#define PWM_PRESCALE_HIGH_MASK 0xf0
#define PWM_CPT_EDGE_MASK 0x03
#define PWM_INT_ACK_MASK 0x1ff
#define STI_MAX_CPT_DEVS 4
#define CPT_DC_MAX 0xff
/* Regfield IDs */
enum
{
/* Bits in PWM_CTRL*/
PWMCLK_PRESCALE_LOW
,
PWMCLK_PRESCALE_HIGH
,
PWM_EN
,
PWM_INT_EN
,
CPTCLK_PRESCALE
,
PWM_OUT_EN
,
PWM_CPT_EN
,
PWM_CPT_INT_EN
,
PWM_CPT_INT_STAT
,
/* Keep last */
MAX_REGFIELDS
};
/*
* Each capture input can be programmed to detect rising-edge, falling-edge,
* either edge or neither egde.
*/
enum
sti_cpt_edge
{
CPT_EDGE_DISABLED
,
CPT_EDGE_RISING
,
CPT_EDGE_FALLING
,
CPT_EDGE_BOTH
,
};
struct
sti_cpt_ddata
{
u32
snapshot
[
3
];
unsigned
int
index
;
struct
mutex
lock
;
wait_queue_head_t
wait
;
};
struct
sti_pwm_compat_data
{
const
struct
reg_field
*
reg_fields
;
unsigned
int
num_chan
;
unsigned
int
pwm_num_devs
;
unsigned
int
cpt_num_devs
;
unsigned
int
max_pwm_cnt
;
unsigned
int
max_prescale
;
};
struct
sti_pwm_chip
{
struct
device
*
dev
;
struct
clk
*
clk
;
unsigned
long
clk_rate
;
struct
clk
*
pwm_
clk
;
struct
clk
*
cpt_clk
;
struct
regmap
*
regmap
;
struct
sti_pwm_compat_data
*
cdata
;
struct
regmap_field
*
prescale_low
;
struct
regmap_field
*
prescale_high
;
struct
regmap_field
*
pwm_en
;
struct
regmap_field
*
pwm_int_en
;
struct
regmap_field
*
pwm_out_en
;
struct
regmap_field
*
pwm_cpt_en
;
struct
regmap_field
*
pwm_cpt_int_en
;
struct
regmap_field
*
pwm_cpt_int_stat
;
struct
pwm_chip
chip
;
struct
pwm_device
*
cur
;
unsigned
long
configured
;
...
...
@@ -64,10 +106,13 @@ struct sti_pwm_chip {
};
static
const
struct
reg_field
sti_pwm_regfields
[
MAX_REGFIELDS
]
=
{
[
PWMCLK_PRESCALE_LOW
]
=
REG_FIELD
(
STI_PWMCR
,
0
,
3
),
[
PWMCLK_PRESCALE_HIGH
]
=
REG_FIELD
(
STI_PWMCR
,
11
,
14
),
[
PWM_EN
]
=
REG_FIELD
(
STI_PWMCR
,
9
,
9
),
[
PWM_INT_EN
]
=
REG_FIELD
(
STI_INTEN
,
0
,
0
),
[
PWMCLK_PRESCALE_LOW
]
=
REG_FIELD
(
STI_PWM_CTRL
,
0
,
3
),
[
PWMCLK_PRESCALE_HIGH
]
=
REG_FIELD
(
STI_PWM_CTRL
,
11
,
14
),
[
CPTCLK_PRESCALE
]
=
REG_FIELD
(
STI_PWM_CTRL
,
4
,
8
),
[
PWM_OUT_EN
]
=
REG_FIELD
(
STI_PWM_CTRL
,
9
,
9
),
[
PWM_CPT_EN
]
=
REG_FIELD
(
STI_PWM_CTRL
,
10
,
10
),
[
PWM_CPT_INT_EN
]
=
REG_FIELD
(
STI_INT_EN
,
1
,
4
),
[
PWM_CPT_INT_STAT
]
=
REG_FIELD
(
STI_INT_STA
,
1
,
4
),
};
static
inline
struct
sti_pwm_chip
*
to_sti_pwmchip
(
struct
pwm_chip
*
chip
)
...
...
@@ -82,61 +127,68 @@ static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period,
unsigned
int
*
prescale
)
{
struct
sti_pwm_compat_data
*
cdata
=
pc
->
cdata
;
unsigned
long
val
;
unsigned
long
clk_rate
;
unsigned
long
value
;
unsigned
int
ps
;
clk_rate
=
clk_get_rate
(
pc
->
pwm_clk
);
if
(
!
clk_rate
)
{
dev_err
(
pc
->
dev
,
"failed to get clock rate
\n
"
);
return
-
EINVAL
;
}
/*
* prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_c
ou
nt + 1)) - 1
* prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_cnt + 1)) - 1
*/
val
=
NSEC_PER_SEC
/
pc
->
clk_rate
;
val
*=
cdata
->
max_pwm_cnt
+
1
;
val
ue
=
NSEC_PER_SEC
/
clk_rate
;
val
ue
*=
cdata
->
max_pwm_cnt
+
1
;
if
(
period
%
val
)
{
if
(
period
%
val
ue
)
return
-
EINVAL
;
}
else
{
ps
=
period
/
val
-
1
;
if
(
ps
>
cdata
->
max_prescale
)
return
-
EINVAL
;
}
ps
=
period
/
value
-
1
;
if
(
ps
>
cdata
->
max_prescale
)
return
-
EINVAL
;
*
prescale
=
ps
;
return
0
;
}
/*
* For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles.
*
The only way to change the period (apart from changing the PWM input clock)
*
is
to change the PWM clock prescaler.
*
The prescaler is of 8 bits, so 256 prescaler values and hence
*
256 possible period values are supported (for a particular clock rate).
*
The requested period will be applied only if it matches one of these
* 256 values.
* For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles.
The
*
only way to change the period (apart from changing the PWM input clock) is
* to change the PWM clock prescaler.
*
*
The prescaler is of 8 bits, so 256 prescaler values and hence 256 possible
*
period values are supported (for a particular clock rate). The requested
*
period will be applied only if it matches one of these
256 values.
*/
static
int
sti_pwm_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
int
duty_ns
,
int
period_ns
)
{
struct
sti_pwm_chip
*
pc
=
to_sti_pwmchip
(
chip
);
struct
sti_pwm_compat_data
*
cdata
=
pc
->
cdata
;
unsigned
int
ncfg
,
value
,
prescale
=
0
;
struct
pwm_device
*
cur
=
pc
->
cur
;
struct
device
*
dev
=
pc
->
dev
;
unsigned
int
prescale
=
0
,
pwmvalx
;
int
ret
;
unsigned
int
ncfg
;
bool
period_same
=
false
;
int
ret
;
ncfg
=
hweight_long
(
pc
->
configured
);
if
(
ncfg
)
period_same
=
(
period_ns
==
pwm_get_period
(
cur
));
/* Allow configuration changes if one of the
* following conditions satisfy.
* 1. No channels have been configured.
* 2. Only one channel has been configured and the new request
* is for the same channel.
* 3. Only one channel has been configured and the new request is
* for a new channel and period of the new channel is same as
* the current configured period.
* 4. More than one channels are configured and period of the new
/*
* Allow configuration changes if one of the following conditions
* satisfy.
* 1. No devices have been configured.
* 2. Only one device has been configured and the new request is for
* the same device.
* 3. Only one device has been configured and the new request is for
* a new device and period of the new device is same as the current
* configured period.
* 4. More than one devices are configured and period of the new
* requestis the same as the current period.
*/
if
(
!
ncfg
||
...
...
@@ -144,7 +196,11 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
((
ncfg
==
1
)
&&
(
pwm
->
hwpwm
!=
cur
->
hwpwm
)
&&
period_same
)
||
((
ncfg
>
1
)
&&
period_same
))
{
/* Enable clock before writing to PWM registers. */
ret
=
clk_enable
(
pc
->
clk
);
ret
=
clk_enable
(
pc
->
pwm_clk
);
if
(
ret
)
return
ret
;
ret
=
clk_enable
(
pc
->
cpt_clk
);
if
(
ret
)
return
ret
;
...
...
@@ -153,15 +209,15 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
if
(
ret
)
goto
clk_dis
;
ret
=
regmap_field_write
(
pc
->
prescale_low
,
prescale
&
PWM_PRESCALE_LOW_MASK
);
value
=
prescale
&
PWM_PRESCALE_LOW_MASK
;
ret
=
regmap_field_write
(
pc
->
prescale_low
,
value
);
if
(
ret
)
goto
clk_dis
;
ret
=
regmap_field_write
(
pc
->
prescale_high
,
(
prescale
&
PWM_PRESCALE_HIGH_MASK
)
>>
4
);
value
=
(
prescale
&
PWM_PRESCALE_HIGH_MASK
)
>>
4
;
ret
=
regmap_field_write
(
pc
->
prescale_high
,
value
);
if
(
ret
)
goto
clk_dis
;
}
...
...
@@ -172,25 +228,26 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* PWM pulse = (max_pwm_count + 1) local cycles,
* that is continuous pulse: signal never goes low.
*/
pwmvalx
=
cdata
->
max_pwm_cnt
*
duty_ns
/
period_ns
;
value
=
cdata
->
max_pwm_cnt
*
duty_ns
/
period_ns
;
ret
=
regmap_write
(
pc
->
regmap
,
STI_DS_REG
(
pwm
->
hwpwm
),
pwmvalx
);
ret
=
regmap_write
(
pc
->
regmap
,
PWM_OUT_VAL
(
pwm
->
hwpwm
),
value
);
if
(
ret
)
goto
clk_dis
;
ret
=
regmap_field_write
(
pc
->
pwm_int_en
,
0
);
ret
=
regmap_field_write
(
pc
->
pwm_
cpt_
int_en
,
0
);
set_bit
(
pwm
->
hwpwm
,
&
pc
->
configured
);
pc
->
cur
=
pwm
;
dev_dbg
(
dev
,
"prescale:%u, period:%i, duty:%i,
pwmvalx
:%u
\n
"
,
prescale
,
period_ns
,
duty_ns
,
pwmvalx
);
dev_dbg
(
dev
,
"prescale:%u, period:%i, duty:%i,
value
:%u
\n
"
,
prescale
,
period_ns
,
duty_ns
,
value
);
}
else
{
return
-
EINVAL
;
}
clk_dis:
clk_disable
(
pc
->
clk
);
clk_disable
(
pc
->
pwm_clk
);
clk_disable
(
pc
->
cpt_clk
);
return
ret
;
}
...
...
@@ -201,23 +258,30 @@ static int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
int
ret
=
0
;
/*
* Since we have a common enable for all PWM
channels,
*
do not enable if
already enabled.
* Since we have a common enable for all PWM
devices, do not enable if
* already enabled.
*/
mutex_lock
(
&
pc
->
sti_pwm_lock
);
if
(
!
pc
->
en_count
)
{
ret
=
clk_enable
(
pc
->
clk
);
ret
=
clk_enable
(
pc
->
pwm_clk
);
if
(
ret
)
goto
out
;
ret
=
clk_enable
(
pc
->
cpt_clk
);
if
(
ret
)
goto
out
;
ret
=
regmap_field_write
(
pc
->
pwm_en
,
1
);
ret
=
regmap_field_write
(
pc
->
pwm_
out_
en
,
1
);
if
(
ret
)
{
dev_err
(
dev
,
"failed to enable PWM device
:
%d
\n
"
,
pwm
->
hwpwm
);
dev_err
(
dev
,
"failed to enable PWM device
%u:
%d
\n
"
,
pwm
->
hwpwm
,
ret
);
goto
out
;
}
}
pc
->
en_count
++
;
out:
mutex_unlock
(
&
pc
->
sti_pwm_lock
);
return
ret
;
...
...
@@ -228,13 +292,17 @@ static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
struct
sti_pwm_chip
*
pc
=
to_sti_pwmchip
(
chip
);
mutex_lock
(
&
pc
->
sti_pwm_lock
);
if
(
--
pc
->
en_count
)
{
mutex_unlock
(
&
pc
->
sti_pwm_lock
);
return
;
}
regmap_field_write
(
pc
->
pwm_en
,
0
);
clk_disable
(
pc
->
clk
);
regmap_field_write
(
pc
->
pwm_out_en
,
0
);
clk_disable
(
pc
->
pwm_clk
);
clk_disable
(
pc
->
cpt_clk
);
mutex_unlock
(
&
pc
->
sti_pwm_lock
);
}
...
...
@@ -245,7 +313,90 @@ static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
clear_bit
(
pwm
->
hwpwm
,
&
pc
->
configured
);
}
static
int
sti_pwm_capture
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_capture
*
result
,
unsigned
long
timeout
)
{
struct
sti_pwm_chip
*
pc
=
to_sti_pwmchip
(
chip
);
struct
sti_pwm_compat_data
*
cdata
=
pc
->
cdata
;
struct
sti_cpt_ddata
*
ddata
=
pwm_get_chip_data
(
pwm
);
struct
device
*
dev
=
pc
->
dev
;
unsigned
int
effective_ticks
;
unsigned
long
long
high
,
low
;
int
ret
;
if
(
pwm
->
hwpwm
>=
cdata
->
cpt_num_devs
)
{
dev_err
(
dev
,
"device %u is not valid
\n
"
,
pwm
->
hwpwm
);
return
-
EINVAL
;
}
mutex_lock
(
&
ddata
->
lock
);
ddata
->
index
=
0
;
/* Prepare capture measurement */
regmap_write
(
pc
->
regmap
,
PWM_CPT_EDGE
(
pwm
->
hwpwm
),
CPT_EDGE_RISING
);
regmap_field_write
(
pc
->
pwm_cpt_int_en
,
BIT
(
pwm
->
hwpwm
));
/* Enable capture */
ret
=
regmap_field_write
(
pc
->
pwm_cpt_en
,
1
);
if
(
ret
)
{
dev_err
(
dev
,
"failed to enable PWM capture %u: %d
\n
"
,
pwm
->
hwpwm
,
ret
);
goto
out
;
}
ret
=
wait_event_interruptible_timeout
(
ddata
->
wait
,
ddata
->
index
>
1
,
msecs_to_jiffies
(
timeout
));
regmap_write
(
pc
->
regmap
,
PWM_CPT_EDGE
(
pwm
->
hwpwm
),
CPT_EDGE_DISABLED
);
if
(
ret
==
-
ERESTARTSYS
)
goto
out
;
switch
(
ddata
->
index
)
{
case
0
:
case
1
:
/*
* Getting here could mean:
* - input signal is constant of less than 1 Hz
* - there is no input signal at all
*
* In such case the frequency is rounded down to 0
*/
result
->
period
=
0
;
result
->
duty_cycle
=
0
;
break
;
case
2
:
/* We have everying we need */
high
=
ddata
->
snapshot
[
1
]
-
ddata
->
snapshot
[
0
];
low
=
ddata
->
snapshot
[
2
]
-
ddata
->
snapshot
[
1
];
effective_ticks
=
clk_get_rate
(
pc
->
cpt_clk
);
result
->
period
=
(
high
+
low
)
*
NSEC_PER_SEC
;
result
->
period
/=
effective_ticks
;
result
->
duty_cycle
=
high
*
NSEC_PER_SEC
;
result
->
duty_cycle
/=
effective_ticks
;
break
;
default:
dev_err
(
dev
,
"internal error
\n
"
);
break
;
}
out:
/* Disable capture */
regmap_field_write
(
pc
->
pwm_cpt_en
,
0
);
mutex_unlock
(
&
ddata
->
lock
);
return
ret
;
}
static
const
struct
pwm_ops
sti_pwm_ops
=
{
.
capture
=
sti_pwm_capture
,
.
config
=
sti_pwm_config
,
.
enable
=
sti_pwm_enable
,
.
disable
=
sti_pwm_disable
,
...
...
@@ -253,17 +404,98 @@ static const struct pwm_ops sti_pwm_ops = {
.
owner
=
THIS_MODULE
,
};
static
irqreturn_t
sti_pwm_interrupt
(
int
irq
,
void
*
data
)
{
struct
sti_pwm_chip
*
pc
=
data
;
struct
device
*
dev
=
pc
->
dev
;
struct
sti_cpt_ddata
*
ddata
;
int
devicenum
;
unsigned
int
cpt_int_stat
;
unsigned
int
reg
;
int
ret
=
IRQ_NONE
;
ret
=
regmap_field_read
(
pc
->
pwm_cpt_int_stat
,
&
cpt_int_stat
);
if
(
ret
)
return
ret
;
while
(
cpt_int_stat
)
{
devicenum
=
ffs
(
cpt_int_stat
)
-
1
;
ddata
=
pwm_get_chip_data
(
&
pc
->
chip
.
pwms
[
devicenum
]);
/*
* Capture input:
* _______ _______
* | | | |
* __| |_________________| |________
* ^0 ^1 ^2
*
* Capture start by the first available rising edge. When a
* capture event occurs, capture value (CPT_VALx) is stored,
* index incremented, capture edge changed.
*
* After the capture, if the index > 1, we have collected the
* necessary data so we signal the thread waiting for it and
* disable the capture by setting capture edge to none
*/
regmap_read
(
pc
->
regmap
,
PWM_CPT_VAL
(
devicenum
),
&
ddata
->
snapshot
[
ddata
->
index
]);
switch
(
ddata
->
index
)
{
case
0
:
case
1
:
regmap_read
(
pc
->
regmap
,
PWM_CPT_EDGE
(
devicenum
),
&
reg
);
reg
^=
PWM_CPT_EDGE_MASK
;
regmap_write
(
pc
->
regmap
,
PWM_CPT_EDGE
(
devicenum
),
reg
);
ddata
->
index
++
;
break
;
case
2
:
regmap_write
(
pc
->
regmap
,
PWM_CPT_EDGE
(
devicenum
),
CPT_EDGE_DISABLED
);
wake_up
(
&
ddata
->
wait
);
break
;
default:
dev_err
(
dev
,
"Internal error
\n
"
);
}
cpt_int_stat
&=
~
BIT_MASK
(
devicenum
);
ret
=
IRQ_HANDLED
;
}
/* Just ACK everything */
regmap_write
(
pc
->
regmap
,
PWM_INT_ACK
,
PWM_INT_ACK_MASK
);
return
ret
;
}
static
int
sti_pwm_probe_dt
(
struct
sti_pwm_chip
*
pc
)
{
struct
device
*
dev
=
pc
->
dev
;
const
struct
reg_field
*
reg_fields
;
struct
device_node
*
np
=
dev
->
of_node
;
struct
sti_pwm_compat_data
*
cdata
=
pc
->
cdata
;
u32
num_chan
;
u32
num_devs
;
int
ret
;
ret
=
of_property_read_u32
(
np
,
"st,pwm-num-chan"
,
&
num_devs
);
if
(
!
ret
)
cdata
->
pwm_num_devs
=
num_devs
;
ret
=
of_property_read_u32
(
np
,
"st,capture-num-chan"
,
&
num_devs
);
if
(
!
ret
)
cdata
->
cpt_num_devs
=
num_devs
;
of_property_read_u32
(
np
,
"st,pwm-num-chan"
,
&
num_chan
);
if
(
num_chan
)
cdata
->
num_chan
=
num_chan
;
if
(
!
cdata
->
pwm_num_devs
&&
!
cdata
->
cpt_num_devs
)
{
dev_err
(
dev
,
"No channels configured
\n
"
);
return
-
EINVAL
;
}
reg_fields
=
cdata
->
reg_fields
;
...
...
@@ -277,15 +509,26 @@ static int sti_pwm_probe_dt(struct sti_pwm_chip *pc)
if
(
IS_ERR
(
pc
->
prescale_high
))
return
PTR_ERR
(
pc
->
prescale_high
);
pc
->
pwm_en
=
devm_regmap_field_alloc
(
dev
,
pc
->
regmap
,
reg_fields
[
PWM_EN
]);
if
(
IS_ERR
(
pc
->
pwm_en
))
return
PTR_ERR
(
pc
->
pwm_en
);
pc
->
pwm_int_en
=
devm_regmap_field_alloc
(
dev
,
pc
->
regmap
,
reg_fields
[
PWM_INT_EN
]);
if
(
IS_ERR
(
pc
->
pwm_int_en
))
return
PTR_ERR
(
pc
->
pwm_int_en
);
pc
->
pwm_out_en
=
devm_regmap_field_alloc
(
dev
,
pc
->
regmap
,
reg_fields
[
PWM_OUT_EN
]);
if
(
IS_ERR
(
pc
->
pwm_out_en
))
return
PTR_ERR
(
pc
->
pwm_out_en
);
pc
->
pwm_cpt_en
=
devm_regmap_field_alloc
(
dev
,
pc
->
regmap
,
reg_fields
[
PWM_CPT_EN
]);
if
(
IS_ERR
(
pc
->
pwm_cpt_en
))
return
PTR_ERR
(
pc
->
pwm_cpt_en
);
pc
->
pwm_cpt_int_en
=
devm_regmap_field_alloc
(
dev
,
pc
->
regmap
,
reg_fields
[
PWM_CPT_INT_EN
]);
if
(
IS_ERR
(
pc
->
pwm_cpt_int_en
))
return
PTR_ERR
(
pc
->
pwm_cpt_int_en
);
pc
->
pwm_cpt_int_stat
=
devm_regmap_field_alloc
(
dev
,
pc
->
regmap
,
reg_fields
[
PWM_CPT_INT_STAT
]);
if
(
PTR_ERR_OR_ZERO
(
pc
->
pwm_cpt_int_stat
))
return
PTR_ERR
(
pc
->
pwm_cpt_int_stat
);
return
0
;
}
...
...
@@ -302,7 +545,8 @@ static int sti_pwm_probe(struct platform_device *pdev)
struct
sti_pwm_compat_data
*
cdata
;
struct
sti_pwm_chip
*
pc
;
struct
resource
*
res
;
int
ret
;
unsigned
int
i
;
int
irq
,
ret
;
pc
=
devm_kzalloc
(
dev
,
sizeof
(
*
pc
),
GFP_KERNEL
);
if
(
!
pc
)
...
...
@@ -323,14 +567,28 @@ static int sti_pwm_probe(struct platform_device *pdev)
if
(
IS_ERR
(
pc
->
regmap
))
return
PTR_ERR
(
pc
->
regmap
);
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
irq
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"Failed to obtain IRQ
\n
"
);
return
irq
;
}
ret
=
devm_request_irq
(
&
pdev
->
dev
,
irq
,
sti_pwm_interrupt
,
0
,
pdev
->
name
,
pc
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"Failed to request IRQ
\n
"
);
return
ret
;
}
/*
* Setup PWM data with default values: some values could be replaced
* with specific ones provided from Device Tree.
*/
cdata
->
reg_fields
=
&
sti_pwm_regfields
[
0
]
;
cdata
->
reg_fields
=
sti_pwm_regfields
;
cdata
->
max_prescale
=
0xff
;
cdata
->
max_pwm_cnt
=
255
;
cdata
->
num_chan
=
1
;
cdata
->
max_pwm_cnt
=
255
;
cdata
->
pwm_num_devs
=
0
;
cdata
->
cpt_num_devs
=
0
;
pc
->
cdata
=
cdata
;
pc
->
dev
=
dev
;
...
...
@@ -341,36 +599,64 @@ static int sti_pwm_probe(struct platform_device *pdev)
if
(
ret
)
return
ret
;
pc
->
clk
=
of_clk_get_by_name
(
dev
->
of_node
,
"pwm"
);
if
(
IS_ERR
(
pc
->
clk
))
{
if
(
!
cdata
->
pwm_num_devs
)
goto
skip_pwm
;
pc
->
pwm_clk
=
of_clk_get_by_name
(
dev
->
of_node
,
"pwm"
);
if
(
IS_ERR
(
pc
->
pwm_clk
))
{
dev_err
(
dev
,
"failed to get PWM clock
\n
"
);
return
PTR_ERR
(
pc
->
clk
);
return
PTR_ERR
(
pc
->
pwm_
clk
);
}
pc
->
clk_rate
=
clk_get_rate
(
pc
->
clk
);
if
(
!
pc
->
clk_rate
)
{
dev_err
(
dev
,
"failed to
get clock rate
\n
"
);
return
-
EINVAL
;
ret
=
clk_prepare
(
pc
->
pwm_
clk
);
if
(
ret
)
{
dev_err
(
dev
,
"failed to
prepare clock
\n
"
);
return
ret
;
}
ret
=
clk_prepare
(
pc
->
clk
);
skip_pwm:
if
(
!
cdata
->
cpt_num_devs
)
goto
skip_cpt
;
pc
->
cpt_clk
=
of_clk_get_by_name
(
dev
->
of_node
,
"capture"
);
if
(
IS_ERR
(
pc
->
cpt_clk
))
{
dev_err
(
dev
,
"failed to get PWM capture clock
\n
"
);
return
PTR_ERR
(
pc
->
cpt_clk
);
}
ret
=
clk_prepare
(
pc
->
cpt_clk
);
if
(
ret
)
{
dev_err
(
dev
,
"failed to prepare clock
\n
"
);
return
ret
;
}
skip_cpt:
pc
->
chip
.
dev
=
dev
;
pc
->
chip
.
ops
=
&
sti_pwm_ops
;
pc
->
chip
.
base
=
-
1
;
pc
->
chip
.
npwm
=
pc
->
cdata
->
num_chan
;
pc
->
chip
.
npwm
=
pc
->
cdata
->
pwm_num_devs
;
pc
->
chip
.
can_sleep
=
true
;
ret
=
pwmchip_add
(
&
pc
->
chip
);
if
(
ret
<
0
)
{
clk_unprepare
(
pc
->
clk
);
clk_unprepare
(
pc
->
pwm_clk
);
clk_unprepare
(
pc
->
cpt_clk
);
return
ret
;
}
for
(
i
=
0
;
i
<
cdata
->
cpt_num_devs
;
i
++
)
{
struct
sti_cpt_ddata
*
ddata
;
ddata
=
devm_kzalloc
(
dev
,
sizeof
(
*
ddata
),
GFP_KERNEL
);
if
(
!
ddata
)
return
-
ENOMEM
;
init_waitqueue_head
(
&
ddata
->
wait
);
mutex_init
(
&
ddata
->
lock
);
pwm_set_chip_data
(
&
pc
->
chip
.
pwms
[
i
],
ddata
);
}
platform_set_drvdata
(
pdev
,
pc
);
return
0
;
...
...
@@ -381,10 +667,11 @@ static int sti_pwm_remove(struct platform_device *pdev)
struct
sti_pwm_chip
*
pc
=
platform_get_drvdata
(
pdev
);
unsigned
int
i
;
for
(
i
=
0
;
i
<
pc
->
cdata
->
num_chan
;
i
++
)
for
(
i
=
0
;
i
<
pc
->
cdata
->
pwm_num_devs
;
i
++
)
pwm_disable
(
&
pc
->
chip
.
pwms
[
i
]);
clk_unprepare
(
pc
->
clk
);
clk_unprepare
(
pc
->
pwm_clk
);
clk_unprepare
(
pc
->
cpt_clk
);
return
pwmchip_remove
(
&
pc
->
chip
);
}
...
...
drivers/pwm/pwm-sun4i.c
浏览文件 @
dc8e6e1e
...
...
@@ -284,6 +284,12 @@ static const struct sun4i_pwm_data sun4i_pwm_data_a20 = {
.
npwm
=
2
,
};
static
const
struct
sun4i_pwm_data
sun4i_pwm_data_h3
=
{
.
has_prescaler_bypass
=
true
,
.
has_rdy
=
true
,
.
npwm
=
1
,
};
static
const
struct
of_device_id
sun4i_pwm_dt_ids
[]
=
{
{
.
compatible
=
"allwinner,sun4i-a10-pwm"
,
...
...
@@ -297,6 +303,9 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = {
},
{
.
compatible
=
"allwinner,sun7i-a20-pwm"
,
.
data
=
&
sun4i_pwm_data_a20
,
},
{
.
compatible
=
"allwinner,sun8i-h3-pwm"
,
.
data
=
&
sun4i_pwm_data_h3
,
},
{
/* sentinel */
},
...
...
drivers/pwm/pwm-tipwmss.c
浏览文件 @
dc8e6e1e
...
...
@@ -34,7 +34,6 @@ static int pwmss_probe(struct platform_device *pdev)
struct
device_node
*
node
=
pdev
->
dev
.
of_node
;
pm_runtime_enable
(
&
pdev
->
dev
);
pm_runtime_get_sync
(
&
pdev
->
dev
);
/* Populate all the child nodes here... */
ret
=
of_platform_populate
(
node
,
NULL
,
NULL
,
&
pdev
->
dev
);
...
...
@@ -46,31 +45,13 @@ static int pwmss_probe(struct platform_device *pdev)
static
int
pwmss_remove
(
struct
platform_device
*
pdev
)
{
pm_runtime_put_sync
(
&
pdev
->
dev
);
pm_runtime_disable
(
&
pdev
->
dev
);
return
0
;
}
#ifdef CONFIG_PM_SLEEP
static
int
pwmss_suspend
(
struct
device
*
dev
)
{
pm_runtime_put_sync
(
dev
);
return
0
;
}
static
int
pwmss_resume
(
struct
device
*
dev
)
{
pm_runtime_get_sync
(
dev
);
return
0
;
}
#endif
static
SIMPLE_DEV_PM_OPS
(
pwmss_pm_ops
,
pwmss_suspend
,
pwmss_resume
);
static
struct
platform_driver
pwmss_driver
=
{
.
driver
=
{
.
name
=
"pwmss"
,
.
pm
=
&
pwmss_pm_ops
,
.
of_match_table
=
pwmss_of_match
,
},
.
probe
=
pwmss_probe
,
...
...
drivers/pwm/pwm-twl.c
浏览文件 @
dc8e6e1e
...
...
@@ -269,6 +269,22 @@ static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
goto
out
;
}
val
|=
TWL6030_PWM_TOGGLE
(
pwm
->
hwpwm
,
TWL6030_PWMXEN
);
ret
=
twl_i2c_write_u8
(
TWL6030_MODULE_ID1
,
val
,
TWL6030_TOGGLE3_REG
);
if
(
ret
<
0
)
{
dev_err
(
chip
->
dev
,
"%s: Failed to disable PWM
\n
"
,
pwm
->
label
);
goto
out
;
}
val
&=
~
TWL6030_PWM_TOGGLE
(
pwm
->
hwpwm
,
TWL6030_PWMXEN
);
ret
=
twl_i2c_write_u8
(
TWL6030_MODULE_ID1
,
val
,
TWL6030_TOGGLE3_REG
);
if
(
ret
<
0
)
{
dev_err
(
chip
->
dev
,
"%s: Failed to disable PWM
\n
"
,
pwm
->
label
);
goto
out
;
}
twl
->
twl6030_toggle3
=
val
;
out:
mutex_unlock
(
&
twl
->
mutex
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录