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
dc8e6e1e
Commit
dc8e6e1e
authored
Sep 08, 2016
by
Thierry Reding
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-4.9/drivers' into for-next
parents
51f01e4c
2fbc487d
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
1158 additions
and
145 deletions
+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
No files found.
Documentation/devicetree/bindings/pwm/pwm-meson.txt
0 → 100644
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
);
...
...
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