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
7755daf5
Commit
7755daf5
authored
Aug 21, 2017
by
Thierry Reding
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-4.14/drivers' into for-next
parents
5771a8c0
08a4d8ec
Changes
21
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
699 additions
and
307 deletions
+699
-307
Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt
Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt
+2
-2
Documentation/devicetree/bindings/pwm/pwm-mediatek.txt
Documentation/devicetree/bindings/pwm/pwm-mediatek.txt
+5
-1
Documentation/devicetree/bindings/pwm/pwm-rockchip.txt
Documentation/devicetree/bindings/pwm/pwm-rockchip.txt
+9
-2
Documentation/devicetree/bindings/pwm/pwm-tiecap.txt
Documentation/devicetree/bindings/pwm/pwm-tiecap.txt
+1
-0
Documentation/devicetree/bindings/pwm/pwm-zx.txt
Documentation/devicetree/bindings/pwm/pwm-zx.txt
+22
-0
Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.txt
Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.txt
+0
-1
drivers/pwm/Kconfig
drivers/pwm/Kconfig
+11
-2
drivers/pwm/Makefile
drivers/pwm/Makefile
+1
-0
drivers/pwm/pwm-bcm2835.c
drivers/pwm/pwm-bcm2835.c
+2
-0
drivers/pwm/pwm-hibvt.c
drivers/pwm/pwm-hibvt.c
+1
-1
drivers/pwm/pwm-mediatek.c
drivers/pwm/pwm-mediatek.c
+54
-24
drivers/pwm/pwm-meson.c
drivers/pwm/pwm-meson.c
+1
-1
drivers/pwm/pwm-pca9685.c
drivers/pwm/pwm-pca9685.c
+7
-7
drivers/pwm/pwm-renesas-tpu.c
drivers/pwm/pwm-renesas-tpu.c
+0
-1
drivers/pwm/pwm-rockchip.c
drivers/pwm/pwm-rockchip.c
+147
-134
drivers/pwm/pwm-samsung.c
drivers/pwm/pwm-samsung.c
+35
-35
drivers/pwm/pwm-tegra.c
drivers/pwm/pwm-tegra.c
+1
-1
drivers/pwm/pwm-tiecap.c
drivers/pwm/pwm-tiecap.c
+48
-42
drivers/pwm/pwm-tiehrpwm.c
drivers/pwm/pwm-tiehrpwm.c
+69
-53
drivers/pwm/pwm-vt8500.c
drivers/pwm/pwm-vt8500.c
+1
-0
drivers/pwm/pwm-zx.c
drivers/pwm/pwm-zx.c
+282
-0
No files found.
Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt
View file @
7755daf5
...
...
@@ -6,7 +6,7 @@ Required properties:
- clocks: This clock defines the base clock frequency of the PWM hardware
system, the period and the duty_cycle of the PWM signal is a multiple of
the base period.
- #pwm-cells: Should be
2
. See pwm.txt in this directory for a description of
- #pwm-cells: Should be
3
. See pwm.txt in this directory for a description of
the cells format.
Examples:
...
...
@@ -15,7 +15,7 @@ pwm@2020c000 {
compatible = "brcm,bcm2835-pwm";
reg = <0x2020c000 0x28>;
clocks = <&clk_pwm>;
#pwm-cells = <
2
>;
#pwm-cells = <
3
>;
};
clocks {
...
...
Documentation/devicetree/bindings/pwm/pwm-mediatek.txt
View file @
7755daf5
...
...
@@ -2,6 +2,8 @@ MediaTek PWM controller
Required properties:
- compatible: should be "mediatek,<name>-pwm":
- "mediatek,mt2712-pwm": found on mt2712 SoC.
- "mediatek,mt7622-pwm": found on mt7622 SoC.
- "mediatek,mt7623-pwm": found on mt7623 SoC.
- reg: physical base address and length of the controller's registers.
- #pwm-cells: must be 2. See pwm.txt in this directory for a description of
...
...
@@ -10,7 +12,9 @@ Required properties:
- clock-names: must contain the following:
- "top": the top clock generator
- "main": clock used by the PWM core
- "pwm1-5": the five per PWM clocks
- "pwm1-8": the eight per PWM clocks for mt2712
- "pwm1-6": the six per PWM clocks for mt7622
- "pwm1-5": the five per PWM clocks for mt7623
- pinctrl-names: Must contain a "default" entry.
- pinctrl-0: One property must exist for each entry in pinctrl-names.
See pinctrl/pinctrl-bindings.txt for details of the property values.
...
...
Documentation/devicetree/bindings/pwm/pwm-rockchip.txt
View file @
7755daf5
...
...
@@ -3,10 +3,17 @@ Rockchip PWM controller
Required properties:
- compatible: should be "rockchip,<name>-pwm"
"rockchip,rk2928-pwm": found on RK29XX,RK3066 and RK3188 SoCs
"rockchip,rk3288-pwm": found on RK3288 SoC
"rockchip,rk3288-pwm": found on RK3288 SOC
"rockchip,rv1108-pwm", "rockchip,rk3288-pwm": found on RV1108 SoC
"rockchip,vop-pwm": found integrated in VOP on RK3288 SoC
- reg: physical base address and length of the controller's registers
- clocks: phandle and clock specifier of the PWM reference clock
- clocks: See ../clock/clock-bindings.txt
- For older hardware (rk2928, rk3066, rk3188, rk3228, rk3288, rk3399):
- There is one clock that's used both to derive the functional clock
for the device and as the bus clock.
- For newer hardware (rk3328 and future socs): specified by name
- "pwm": This is used to derive the functional clock.
- "pclk": This is the APB bus clock.
- #pwm-cells: must be 2 (rk2928) or 3 (rk3288). See pwm.txt in this directory
for a description of the cell format.
...
...
Documentation/devicetree/bindings/pwm/pwm-tiecap.txt
View file @
7755daf5
...
...
@@ -6,6 +6,7 @@ Required properties:
for am4372 - compatible = "ti,am4372-ecap", "ti,am3352-ecap", "ti,am33xx-ecap";
for da850 - compatible = "ti,da850-ecap", "ti,am3352-ecap", "ti,am33xx-ecap";
for dra746 - compatible = "ti,dra746-ecap", "ti,am3352-ecap";
for 66ak2g - compatible = "ti,k2g-ecap", "ti,am3352-ecap";
- #pwm-cells: should be 3. See pwm.txt in this directory for a description of
the cells format. The PWM channel index ranges from 0 to 4. The only third
cell flag supported by this binding is PWM_POLARITY_INVERTED.
...
...
Documentation/devicetree/bindings/pwm/pwm-zx.txt
0 → 100644
View file @
7755daf5
ZTE ZX PWM controller
Required properties:
- compatible: Should be "zte,zx296718-pwm".
- reg: Physical base address and length of the controller's registers.
- clocks : The phandle and specifier referencing the controller's clocks.
- clock-names: "pclk" for PCLK, "wclk" for WCLK to the PWM controller. The
PCLK is for register access, while WCLK is the reference clock for
calculating period and duty cycles.
- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
the cells format.
Example:
pwm: pwm@1439000 {
compatible = "zte,zx296718-pwm";
reg = <0x1439000 0x1000>;
clocks = <&lsp1crm LSP1_PWM_PCLK>,
<&lsp1crm LSP1_PWM_WCLK>;
clock-names = "pclk", "wclk";
#pwm-cells = <3>;
};
Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.txt
View file @
7755daf5
...
...
@@ -6,7 +6,6 @@ Required Properties:
- "renesas,tpu-r8a73a4": for R8A77A4 (R-Mobile APE6) compatible PWM controller.
- "renesas,tpu-r8a7740": for R8A7740 (R-Mobile A1) compatible PWM controller.
- "renesas,tpu-r8a7790": for R8A7790 (R-Car H2) compatible PWM controller.
- "renesas,tpu-sh7372": for SH7372 (SH-Mobile AP4) compatible PWM controller.
- "renesas,tpu": for generic R-Car TPU PWM controller.
- reg: Base address and length of each memory resource used by the PWM
...
...
drivers/pwm/Kconfig
View file @
7755daf5
...
...
@@ -300,7 +300,7 @@ config PWM_MEDIATEK
Generic PWM framework driver for Mediatek ARM SoC.
To compile this driver as a module, choose M here: the module
will be called pwm-m
xs
.
will be called pwm-m
ediatek
.
config PWM_MXS
tristate "Freescale MXS PWM support"
...
...
@@ -446,7 +446,7 @@ config PWM_TEGRA
config PWM_TIECAP
tristate "ECAP PWM support"
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX
|| ARCH_KEYSTONE
help
PWM driver support for the ECAP APWM controller found on AM33XX
TI SOC
...
...
@@ -500,4 +500,13 @@ config PWM_VT8500
To compile this driver as a module, choose M here: the module
will be called pwm-vt8500.
config PWM_ZX
tristate "ZTE ZX PWM support"
depends on ARCH_ZX
help
Generic PWM framework driver for ZTE ZX family SoCs.
To compile this driver as a module, choose M here: the module
will be called pwm-zx.
endif
drivers/pwm/Makefile
View file @
7755daf5
...
...
@@ -49,3 +49,4 @@ obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o
obj-$(CONFIG_PWM_TWL)
+=
pwm-twl.o
obj-$(CONFIG_PWM_TWL_LED)
+=
pwm-twl-led.o
obj-$(CONFIG_PWM_VT8500)
+=
pwm-vt8500.o
obj-$(CONFIG_PWM_ZX)
+=
pwm-zx.o
drivers/pwm/pwm-bcm2835.c
View file @
7755daf5
...
...
@@ -167,6 +167,8 @@ static int bcm2835_pwm_probe(struct platform_device *pdev)
pc
->
chip
.
dev
=
&
pdev
->
dev
;
pc
->
chip
.
ops
=
&
bcm2835_pwm_ops
;
pc
->
chip
.
npwm
=
2
;
pc
->
chip
.
of_xlate
=
of_pwm_xlate_with_flags
;
pc
->
chip
.
of_pwm_n_cells
=
3
;
platform_set_drvdata
(
pdev
,
pc
);
...
...
drivers/pwm/pwm-hibvt.c
View file @
7755daf5
...
...
@@ -208,7 +208,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev)
if
(
ret
<
0
)
return
ret
;
pwm_chip
->
rstc
=
devm_reset_control_get
(
&
pdev
->
dev
,
NULL
);
pwm_chip
->
rstc
=
devm_reset_control_get
_exclusive
(
&
pdev
->
dev
,
NULL
);
if
(
IS_ERR
(
pwm_chip
->
rstc
))
{
clk_disable_unprepare
(
pwm_chip
->
clk
);
return
PTR_ERR
(
pwm_chip
->
rstc
);
...
...
drivers/pwm/pwm-mediatek.c
View file @
7755daf5
...
...
@@ -2,6 +2,7 @@
* Mediatek Pulse Width Modulator driver
*
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
* Copyright (C) 2017 Zhi Mao <zhi.mao@mediatek.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
...
...
@@ -29,6 +30,8 @@
#define PWMDWIDTH 0x2c
#define PWMTHRES 0x30
#define PWM_CLK_DIV_MAX 7
enum
{
MTK_CLK_MAIN
=
0
,
MTK_CLK_TOP
,
...
...
@@ -61,6 +64,42 @@ static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip)
return
container_of
(
chip
,
struct
mtk_pwm_chip
,
chip
);
}
static
int
mtk_pwm_clk_enable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
mtk_pwm_chip
*
pc
=
to_mtk_pwm_chip
(
chip
);
int
ret
;
ret
=
clk_prepare_enable
(
pc
->
clks
[
MTK_CLK_TOP
]);
if
(
ret
<
0
)
return
ret
;
ret
=
clk_prepare_enable
(
pc
->
clks
[
MTK_CLK_MAIN
]);
if
(
ret
<
0
)
goto
disable_clk_top
;
ret
=
clk_prepare_enable
(
pc
->
clks
[
MTK_CLK_PWM1
+
pwm
->
hwpwm
]);
if
(
ret
<
0
)
goto
disable_clk_main
;
return
0
;
disable_clk_main:
clk_disable_unprepare
(
pc
->
clks
[
MTK_CLK_MAIN
]);
disable_clk_top:
clk_disable_unprepare
(
pc
->
clks
[
MTK_CLK_TOP
]);
return
ret
;
}
static
void
mtk_pwm_clk_disable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
mtk_pwm_chip
*
pc
=
to_mtk_pwm_chip
(
chip
);
clk_disable_unprepare
(
pc
->
clks
[
MTK_CLK_PWM1
+
pwm
->
hwpwm
]);
clk_disable_unprepare
(
pc
->
clks
[
MTK_CLK_MAIN
]);
clk_disable_unprepare
(
pc
->
clks
[
MTK_CLK_TOP
]);
}
static
inline
u32
mtk_pwm_readl
(
struct
mtk_pwm_chip
*
chip
,
unsigned
int
num
,
unsigned
int
offset
)
{
...
...
@@ -80,6 +119,11 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
struct
mtk_pwm_chip
*
pc
=
to_mtk_pwm_chip
(
chip
);
struct
clk
*
clk
=
pc
->
clks
[
MTK_CLK_PWM1
+
pwm
->
hwpwm
];
u32
resolution
,
clkdiv
=
0
;
int
ret
;
ret
=
mtk_pwm_clk_enable
(
chip
,
pwm
);
if
(
ret
<
0
)
return
ret
;
resolution
=
NSEC_PER_SEC
/
clk_get_rate
(
clk
);
...
...
@@ -88,13 +132,18 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
clkdiv
++
;
}
if
(
clkdiv
>
7
)
if
(
clkdiv
>
PWM_CLK_DIV_MAX
)
{
mtk_pwm_clk_disable
(
chip
,
pwm
);
dev_err
(
chip
->
dev
,
"period %d not supported
\n
"
,
period_ns
);
return
-
EINVAL
;
}
mtk_pwm_writel
(
pc
,
pwm
->
hwpwm
,
PWMCON
,
BIT
(
15
)
|
BIT
(
3
)
|
clkdiv
);
mtk_pwm_writel
(
pc
,
pwm
->
hwpwm
,
PWMCON
,
BIT
(
15
)
|
clkdiv
);
mtk_pwm_writel
(
pc
,
pwm
->
hwpwm
,
PWMDWIDTH
,
period_ns
/
resolution
);
mtk_pwm_writel
(
pc
,
pwm
->
hwpwm
,
PWMTHRES
,
duty_ns
/
resolution
);
mtk_pwm_clk_disable
(
chip
,
pwm
);
return
0
;
}
...
...
@@ -104,7 +153,7 @@ static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
u32
value
;
int
ret
;
ret
=
clk_prepare
(
pc
->
clks
[
MTK_CLK_PWM1
+
pwm
->
hwpwm
]
);
ret
=
mtk_pwm_clk_enable
(
chip
,
pwm
);
if
(
ret
<
0
)
return
ret
;
...
...
@@ -124,7 +173,7 @@ static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
value
&=
~
BIT
(
pwm
->
hwpwm
);
writel
(
value
,
pc
->
regs
);
clk_unprepare
(
pc
->
clks
[
MTK_CLK_PWM1
+
pwm
->
hwpwm
]
);
mtk_pwm_clk_disable
(
chip
,
pwm
);
}
static
const
struct
pwm_ops
mtk_pwm_ops
=
{
...
...
@@ -156,14 +205,6 @@ static int mtk_pwm_probe(struct platform_device *pdev)
return
PTR_ERR
(
pc
->
clks
[
i
]);
}
ret
=
clk_prepare
(
pc
->
clks
[
MTK_CLK_TOP
]);
if
(
ret
<
0
)
return
ret
;
ret
=
clk_prepare
(
pc
->
clks
[
MTK_CLK_MAIN
]);
if
(
ret
<
0
)
goto
disable_clk_top
;
platform_set_drvdata
(
pdev
,
pc
);
pc
->
chip
.
dev
=
&
pdev
->
dev
;
...
...
@@ -174,26 +215,15 @@ static int mtk_pwm_probe(struct platform_device *pdev)
ret
=
pwmchip_add
(
&
pc
->
chip
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"pwmchip_add() failed: %d
\n
"
,
ret
);
goto
disable_clk_main
;
return
ret
;
}
return
0
;
disable_clk_main:
clk_unprepare
(
pc
->
clks
[
MTK_CLK_MAIN
]);
disable_clk_top:
clk_unprepare
(
pc
->
clks
[
MTK_CLK_TOP
]);
return
ret
;
}
static
int
mtk_pwm_remove
(
struct
platform_device
*
pdev
)
{
struct
mtk_pwm_chip
*
pc
=
platform_get_drvdata
(
pdev
);
unsigned
int
i
;
for
(
i
=
0
;
i
<
pc
->
chip
.
npwm
;
i
++
)
pwm_disable
(
&
pc
->
chip
.
pwms
[
i
]);
return
pwmchip_remove
(
&
pc
->
chip
);
}
...
...
drivers/pwm/pwm-meson.c
View file @
7755daf5
...
...
@@ -441,7 +441,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson,
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
);
snprintf
(
name
,
sizeof
(
name
),
"%
pOF#mux%u"
,
np
,
i
);
init
.
name
=
name
;
init
.
ops
=
&
clk_mux_ops
;
...
...
drivers/pwm/pwm-pca9685.c
View file @
7755daf5
...
...
@@ -241,11 +241,11 @@ static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
}
#endif
static
void
pca9685_set_sleep_mode
(
struct
pca9685
*
pca
,
int
sleep
)
static
void
pca9685_set_sleep_mode
(
struct
pca9685
*
pca
,
bool
enable
)
{
regmap_update_bits
(
pca
->
regmap
,
PCA9685_MODE1
,
MODE1_SLEEP
,
sleep
?
MODE1_SLEEP
:
0
);
if
(
!
sleep
)
{
MODE1_SLEEP
,
enable
?
MODE1_SLEEP
:
0
);
if
(
!
enable
)
{
/* Wait 500us for the oscillator to be back up */
udelay
(
500
);
}
...
...
@@ -272,13 +272,13 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* state is guaranteed active here.
*/
/* Put chip into sleep mode */
pca9685_set_sleep_mode
(
pca
,
1
);
pca9685_set_sleep_mode
(
pca
,
true
);
/* Change the chip-wide output frequency */
regmap_write
(
pca
->
regmap
,
PCA9685_PRESCALE
,
prescale
);
/* Wake the chip up */
pca9685_set_sleep_mode
(
pca
,
0
);
pca9685_set_sleep_mode
(
pca
,
false
);
pca
->
period_ns
=
period_ns
;
}
else
{
...
...
@@ -534,7 +534,7 @@ static int pca9685_pwm_runtime_suspend(struct device *dev)
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
pca9685
*
pca
=
i2c_get_clientdata
(
client
);
pca9685_set_sleep_mode
(
pca
,
1
);
pca9685_set_sleep_mode
(
pca
,
true
);
return
0
;
}
...
...
@@ -543,7 +543,7 @@ static int pca9685_pwm_runtime_resume(struct device *dev)
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
pca9685
*
pca
=
i2c_get_clientdata
(
client
);
pca9685_set_sleep_mode
(
pca
,
0
);
pca9685_set_sleep_mode
(
pca
,
false
);
return
0
;
}
#endif
...
...
drivers/pwm/pwm-renesas-tpu.c
View file @
7755daf5
...
...
@@ -455,7 +455,6 @@ static const struct of_device_id tpu_of_table[] = {
{
.
compatible
=
"renesas,tpu-r8a73a4"
,
},
{
.
compatible
=
"renesas,tpu-r8a7740"
,
},
{
.
compatible
=
"renesas,tpu-r8a7790"
,
},
{
.
compatible
=
"renesas,tpu-sh7372"
,
},
{
.
compatible
=
"renesas,tpu"
,
},
{
},
};
...
...
drivers/pwm/pwm-rockchip.c
View file @
7755daf5
...
...
@@ -27,12 +27,15 @@
#define PWM_DUTY_NEGATIVE (0 << 3)
#define PWM_INACTIVE_NEGATIVE (0 << 4)
#define PWM_INACTIVE_POSITIVE (1 << 4)
#define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE)
#define PWM_OUTPUT_LEFT (0 << 5)
#define PWM_LOCK_EN (1 << 6)
#define PWM_LP_DISABLE (0 << 8)
struct
rockchip_pwm_chip
{
struct
pwm_chip
chip
;
struct
clk
*
clk
;
struct
clk
*
pclk
;
const
struct
rockchip_pwm_data
*
data
;
void
__iomem
*
base
;
};
...
...
@@ -48,13 +51,8 @@ struct rockchip_pwm_data {
struct
rockchip_pwm_regs
regs
;
unsigned
int
prescaler
;
bool
supports_polarity
;
const
struct
pwm_ops
*
ops
;
void
(
*
set_enable
)(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
bool
enable
,
enum
pwm_polarity
polarity
);
void
(
*
get_state
)(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
);
bool
supports_lock
;
u32
enable_conf
;
};
static
inline
struct
rockchip_pwm_chip
*
to_rockchip_pwm_chip
(
struct
pwm_chip
*
c
)
...
...
@@ -62,90 +60,18 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
return
container_of
(
c
,
struct
rockchip_pwm_chip
,
chip
);
}
static
void
rockchip_pwm_set_enable_v1
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
bool
enable
,
enum
pwm_polarity
polarity
)
{
struct
rockchip_pwm_chip
*
pc
=
to_rockchip_pwm_chip
(
chip
);
u32
enable_conf
=
PWM_CTRL_OUTPUT_EN
|
PWM_CTRL_TIMER_EN
;
u32
val
;
val
=
readl_relaxed
(
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
if
(
enable
)
val
|=
enable_conf
;
else
val
&=
~
enable_conf
;
writel_relaxed
(
val
,
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
}
static
void
rockchip_pwm_get_state_v1
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
rockchip_pwm_chip
*
pc
=
to_rockchip_pwm_chip
(
chip
);
u32
enable_conf
=
PWM_CTRL_OUTPUT_EN
|
PWM_CTRL_TIMER_EN
;
u32
val
;
val
=
readl_relaxed
(
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
if
((
val
&
enable_conf
)
==
enable_conf
)
state
->
enabled
=
true
;
}
static
void
rockchip_pwm_set_enable_v2
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
bool
enable
,
enum
pwm_polarity
polarity
)
{
struct
rockchip_pwm_chip
*
pc
=
to_rockchip_pwm_chip
(
chip
);
u32
enable_conf
=
PWM_OUTPUT_LEFT
|
PWM_LP_DISABLE
|
PWM_ENABLE
|
PWM_CONTINUOUS
;
u32
val
;
if
(
polarity
==
PWM_POLARITY_INVERSED
)
enable_conf
|=
PWM_DUTY_NEGATIVE
|
PWM_INACTIVE_POSITIVE
;
else
enable_conf
|=
PWM_DUTY_POSITIVE
|
PWM_INACTIVE_NEGATIVE
;
val
=
readl_relaxed
(
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
if
(
enable
)
val
|=
enable_conf
;
else
val
&=
~
enable_conf
;
writel_relaxed
(
val
,
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
}
static
void
rockchip_pwm_get_state_v2
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
rockchip_pwm_chip
*
pc
=
to_rockchip_pwm_chip
(
chip
);
u32
enable_conf
=
PWM_OUTPUT_LEFT
|
PWM_LP_DISABLE
|
PWM_ENABLE
|
PWM_CONTINUOUS
;
u32
val
;
val
=
readl_relaxed
(
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
if
((
val
&
enable_conf
)
!=
enable_conf
)
return
;
state
->
enabled
=
true
;
if
(
!
(
val
&
PWM_DUTY_POSITIVE
))
state
->
polarity
=
PWM_POLARITY_INVERSED
;
}
static
void
rockchip_pwm_get_state
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
rockchip_pwm_chip
*
pc
=
to_rockchip_pwm_chip
(
chip
);
u32
enable_conf
=
pc
->
data
->
enable_conf
;
unsigned
long
clk_rate
;
u64
tmp
;
u32
val
;
int
ret
;
ret
=
clk_enable
(
pc
->
clk
);
ret
=
clk_enable
(
pc
->
p
clk
);
if
(
ret
)
return
;
...
...
@@ -159,17 +85,29 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip,
tmp
*=
pc
->
data
->
prescaler
*
NSEC_PER_SEC
;
state
->
duty_cycle
=
DIV_ROUND_CLOSEST_ULL
(
tmp
,
clk_rate
);
pc
->
data
->
get_state
(
chip
,
pwm
,
state
);
val
=
readl_relaxed
(
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
if
(
pc
->
data
->
supports_polarity
)
state
->
enabled
=
((
val
&
enable_conf
)
!=
enable_conf
)
?
false
:
true
;
else
state
->
enabled
=
((
val
&
enable_conf
)
==
enable_conf
)
?
true
:
false
;
if
(
pc
->
data
->
supports_polarity
)
{
if
(
!
(
val
&
PWM_DUTY_POSITIVE
))
state
->
polarity
=
PWM_POLARITY_INVERSED
;
}
clk_disable
(
pc
->
clk
);
clk_disable
(
pc
->
p
clk
);
}
static
int
rockchip_pwm_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
static
void
rockchip_pwm_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
rockchip_pwm_chip
*
pc
=
to_rockchip_pwm_chip
(
chip
);
unsigned
long
period
,
duty
;
u64
clk_rate
,
div
;
u32
ctrl
;
clk_rate
=
clk_get_rate
(
pc
->
clk
);
...
...
@@ -178,26 +116,53 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* bits, every possible input period can be obtained using the
* default prescaler value for all practical clock rate values.
*/
div
=
clk_rate
*
period_ns
;
div
=
clk_rate
*
state
->
period
;
period
=
DIV_ROUND_CLOSEST_ULL
(
div
,
pc
->
data
->
prescaler
*
NSEC_PER_SEC
);
div
=
clk_rate
*
duty_ns
;
div
=
clk_rate
*
state
->
duty_cycle
;
duty
=
DIV_ROUND_CLOSEST_ULL
(
div
,
pc
->
data
->
prescaler
*
NSEC_PER_SEC
);
/*
* Lock the period and duty of previous configuration, then
* change the duty and period, that would not be effective.
*/
ctrl
=
readl_relaxed
(
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
if
(
pc
->
data
->
supports_lock
)
{
ctrl
|=
PWM_LOCK_EN
;
writel_relaxed
(
ctrl
,
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
}
writel
(
period
,
pc
->
base
+
pc
->
data
->
regs
.
period
);
writel
(
duty
,
pc
->
base
+
pc
->
data
->
regs
.
duty
);
return
0
;
if
(
pc
->
data
->
supports_polarity
)
{
ctrl
&=
~
PWM_POLARITY_MASK
;
if
(
state
->
polarity
==
PWM_POLARITY_INVERSED
)
ctrl
|=
PWM_DUTY_NEGATIVE
|
PWM_INACTIVE_POSITIVE
;
else
ctrl
|=
PWM_DUTY_POSITIVE
|
PWM_INACTIVE_NEGATIVE
;
}
/*
* Unlock and set polarity at the same time,
* the configuration of duty, period and polarity
* would be effective together at next period.
*/
if
(
pc
->
data
->
supports_lock
)
ctrl
&=
~
PWM_LOCK_EN
;
writel
(
ctrl
,
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
}
static
int
rockchip_pwm_enable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
bool
enable
,
enum
pwm_polarity
polarity
)
bool
enable
)
{
struct
rockchip_pwm_chip
*
pc
=
to_rockchip_pwm_chip
(
chip
);
u32
enable_conf
=
pc
->
data
->
enable_conf
;
int
ret
;
u32
val
;
if
(
enable
)
{
ret
=
clk_enable
(
pc
->
clk
);
...
...
@@ -205,7 +170,14 @@ static int rockchip_pwm_enable(struct pwm_chip *chip,
return
ret
;
}
pc
->
data
->
set_enable
(
chip
,
pwm
,
enable
,
polarity
);
val
=
readl_relaxed
(
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
if
(
enable
)
val
|=
enable_conf
;
else
val
&=
~
enable_conf
;
writel_relaxed
(
val
,
pc
->
base
+
pc
->
data
->
regs
.
ctrl
);
if
(
!
enable
)
clk_disable
(
pc
->
clk
);
...
...
@@ -219,33 +191,26 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
struct
rockchip_pwm_chip
*
pc
=
to_rockchip_pwm_chip
(
chip
);
struct
pwm_state
curstate
;
bool
enabled
;
int
ret
;
int
ret
=
0
;
pwm_get_state
(
pwm
,
&
curstate
);
enabled
=
curstate
.
enabled
;
ret
=
clk_enable
(
pc
->
clk
);
ret
=
clk_enable
(
pc
->
pclk
);
if
(
ret
)
return
ret
;
if
(
state
->
polarity
!=
curstate
.
polarity
&&
enabled
)
{
ret
=
rockchip_pwm_enable
(
chip
,
pwm
,
false
,
state
->
polarity
);
pwm_get_state
(
pwm
,
&
curstate
);
enabled
=
curstate
.
enabled
;
if
(
state
->
polarity
!=
curstate
.
polarity
&&
enabled
&&
!
pc
->
data
->
supports_lock
)
{
ret
=
rockchip_pwm_enable
(
chip
,
pwm
,
false
);
if
(
ret
)
goto
out
;
enabled
=
false
;
}
ret
=
rockchip_pwm_config
(
chip
,
pwm
,
state
->
duty_cycle
,
state
->
period
);
if
(
ret
)
{
if
(
enabled
!=
curstate
.
enabled
)
rockchip_pwm_enable
(
chip
,
pwm
,
!
enabled
,
state
->
polarity
);
goto
out
;
}
rockchip_pwm_config
(
chip
,
pwm
,
state
);
if
(
state
->
enabled
!=
enabled
)
{
ret
=
rockchip_pwm_enable
(
chip
,
pwm
,
state
->
enabled
,
state
->
polarity
);
ret
=
rockchip_pwm_enable
(
chip
,
pwm
,
state
->
enabled
);
if
(
ret
)
goto
out
;
}
...
...
@@ -257,18 +222,12 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
rockchip_pwm_get_state
(
chip
,
pwm
,
state
);
out:
clk_disable
(
pc
->
clk
);
clk_disable
(
pc
->
p
clk
);
return
ret
;
}
static
const
struct
pwm_ops
rockchip_pwm_ops_v1
=
{
.
get_state
=
rockchip_pwm_get_state
,
.
apply
=
rockchip_pwm_apply
,
.
owner
=
THIS_MODULE
,
};
static
const
struct
pwm_ops
rockchip_pwm_ops_v2
=
{
static
const
struct
pwm_ops
rockchip_pwm_ops
=
{
.
get_state
=
rockchip_pwm_get_state
,
.
apply
=
rockchip_pwm_apply
,
.
owner
=
THIS_MODULE
,
...
...
@@ -282,9 +241,9 @@ static const struct rockchip_pwm_data pwm_data_v1 = {
.
ctrl
=
0x0c
,
},
.
prescaler
=
2
,
.
ops
=
&
rockchip_pwm_ops_v1
,
.
s
et_enable
=
rockchip_pwm_set_enable_v1
,
.
get_state
=
rockchip_pwm_get_state_v1
,
.
supports_polarity
=
false
,
.
s
upports_lock
=
false
,
.
enable_conf
=
PWM_CTRL_OUTPUT_EN
|
PWM_CTRL_TIMER_EN
,
};
static
const
struct
rockchip_pwm_data
pwm_data_v2
=
{
...
...
@@ -296,9 +255,9 @@ static const struct rockchip_pwm_data pwm_data_v2 = {
},
.
prescaler
=
1
,
.
supports_polarity
=
true
,
.
ops
=
&
rockchip_pwm_ops_v2
,
.
set_enable
=
rockchip_pwm_set_enable_v2
,
.
get_state
=
rockchip_pwm_get_state_v2
,
.
supports_lock
=
false
,
.
enable_conf
=
PWM_OUTPUT_LEFT
|
PWM_LP_DISABLE
|
PWM_ENABLE
|
PWM_CONTINUOUS
,
};
static
const
struct
rockchip_pwm_data
pwm_data_vop
=
{
...
...
@@ -310,15 +269,30 @@ static const struct rockchip_pwm_data pwm_data_vop = {
},
.
prescaler
=
1
,
.
supports_polarity
=
true
,
.
ops
=
&
rockchip_pwm_ops_v2
,
.
set_enable
=
rockchip_pwm_set_enable_v2
,
.
get_state
=
rockchip_pwm_get_state_v2
,
.
supports_lock
=
false
,
.
enable_conf
=
PWM_OUTPUT_LEFT
|
PWM_LP_DISABLE
|
PWM_ENABLE
|
PWM_CONTINUOUS
,
};
static
const
struct
rockchip_pwm_data
pwm_data_v3
=
{
.
regs
=
{
.
duty
=
0x08
,
.
period
=
0x04
,
.
cntr
=
0x00
,
.
ctrl
=
0x0c
,
},
.
prescaler
=
1
,
.
supports_polarity
=
true
,
.
supports_lock
=
true
,
.
enable_conf
=
PWM_OUTPUT_LEFT
|
PWM_LP_DISABLE
|
PWM_ENABLE
|
PWM_CONTINUOUS
,
};
static
const
struct
of_device_id
rockchip_pwm_dt_ids
[]
=
{
{
.
compatible
=
"rockchip,rk2928-pwm"
,
.
data
=
&
pwm_data_v1
},
{
.
compatible
=
"rockchip,rk3288-pwm"
,
.
data
=
&
pwm_data_v2
},
{
.
compatible
=
"rockchip,vop-pwm"
,
.
data
=
&
pwm_data_vop
},
{
.
compatible
=
"rockchip,rk3328-pwm"
,
.
data
=
&
pwm_data_v3
},
{
/* sentinel */
}
};
MODULE_DEVICE_TABLE
(
of
,
rockchip_pwm_dt_ids
);
...
...
@@ -328,7 +302,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
const
struct
of_device_id
*
id
;
struct
rockchip_pwm_chip
*
pc
;
struct
resource
*
r
;
int
ret
;
int
ret
,
count
;
id
=
of_match_device
(
rockchip_pwm_dt_ids
,
&
pdev
->
dev
);
if
(
!
id
)
...
...
@@ -343,19 +317,49 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
if
(
IS_ERR
(
pc
->
base
))
return
PTR_ERR
(
pc
->
base
);
pc
->
clk
=
devm_clk_get
(
&
pdev
->
dev
,
"pwm"
);
if
(
IS_ERR
(
pc
->
clk
))
{
pc
->
clk
=
devm_clk_get
(
&
pdev
->
dev
,
NULL
);
if
(
IS_ERR
(
pc
->
clk
))
return
PTR_ERR
(
pc
->
clk
);
if
(
IS_ERR
(
pc
->
clk
))
{
ret
=
PTR_ERR
(
pc
->
clk
);
if
(
ret
!=
-
EPROBE_DEFER
)
dev_err
(
&
pdev
->
dev
,
"Can't get bus clk: %d
\n
"
,
ret
);
return
ret
;
}
}
count
=
of_count_phandle_with_args
(
pdev
->
dev
.
of_node
,
"clocks"
,
"#clock-cells"
);
if
(
count
==
2
)
pc
->
pclk
=
devm_clk_get
(
&
pdev
->
dev
,
"pclk"
);
else
pc
->
pclk
=
pc
->
clk
;
if
(
IS_ERR
(
pc
->
pclk
))
{
ret
=
PTR_ERR
(
pc
->
pclk
);
if
(
ret
!=
-
EPROBE_DEFER
)
dev_err
(
&
pdev
->
dev
,
"Can't get APB clk: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
clk_prepare_enable
(
pc
->
clk
);
if
(
ret
)
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"Can't prepare enable bus clk: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
clk_prepare
(
pc
->
pclk
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"Can't prepare APB clk: %d
\n
"
,
ret
);
goto
err_clk
;
}
platform_set_drvdata
(
pdev
,
pc
);
pc
->
data
=
id
->
data
;
pc
->
chip
.
dev
=
&
pdev
->
dev
;
pc
->
chip
.
ops
=
pc
->
data
->
ops
;
pc
->
chip
.
ops
=
&
rockchip_pwm_
ops
;
pc
->
chip
.
base
=
-
1
;
pc
->
chip
.
npwm
=
1
;
...
...
@@ -368,12 +372,20 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
if
(
ret
<
0
)
{
clk_unprepare
(
pc
->
clk
);
dev_err
(
&
pdev
->
dev
,
"pwmchip_add() failed: %d
\n
"
,
ret
);
goto
err_pclk
;
}
/* Keep the PWM clk enabled if the PWM appears to be up and running. */
if
(
!
pwm_is_enabled
(
pc
->
chip
.
pwms
))
clk_disable
(
pc
->
clk
);
return
0
;
err_pclk:
clk_unprepare
(
pc
->
pclk
);
err_clk:
clk_disable_unprepare
(
pc
->
clk
);
return
ret
;
}
...
...
@@ -395,6 +407,7 @@ static int rockchip_pwm_remove(struct platform_device *pdev)
if
(
pwm_is_enabled
(
pc
->
chip
.
pwms
))
clk_disable
(
pc
->
clk
);
clk_unprepare
(
pc
->
pclk
);
clk_unprepare
(
pc
->
clk
);
return
pwmchip_remove
(
&
pc
->
chip
);
...
...
drivers/pwm/pwm-samsung.c
View file @
7755daf5
...
...
@@ -3,6 +3,7 @@
* Copyright (c) 2008 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
* Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com>
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* PWM driver for Samsung SoCs
*
...
...
@@ -74,6 +75,7 @@ struct samsung_pwm_channel {
* @chip: generic PWM chip
* @variant: local copy of hardware variant data
* @inverter_mask: inverter status for all channels - one bit per channel
* @disabled_mask: disabled status for all channels - one bit per channel
* @base: base address of mapped PWM registers
* @base_clk: base clock used to drive the timers
* @tclk0: external clock 0 (can be ERR_PTR if not present)
...
...
@@ -83,6 +85,7 @@ struct samsung_pwm_chip {
struct
pwm_chip
chip
;
struct
samsung_pwm_variant
variant
;
u8
inverter_mask
;
u8
disabled_mask
;
void
__iomem
*
base
;
struct
clk
*
base_clk
;
...
...
@@ -257,6 +260,8 @@ static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm)
tcon
|=
TCON_START
(
tcon_chan
)
|
TCON_AUTORELOAD
(
tcon_chan
);
writel
(
tcon
,
our_chip
->
base
+
REG_TCON
);
our_chip
->
disabled_mask
&=
~
BIT
(
pwm
->
hwpwm
);
spin_unlock_irqrestore
(
&
samsung_pwm_lock
,
flags
);
return
0
;
...
...
@@ -275,6 +280,8 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm)
tcon
&=
~
TCON_AUTORELOAD
(
tcon_chan
);
writel
(
tcon
,
our_chip
->
base
+
REG_TCON
);
our_chip
->
disabled_mask
|=
BIT
(
pwm
->
hwpwm
);
spin_unlock_irqrestore
(
&
samsung_pwm_lock
,
flags
);
}
...
...
@@ -297,8 +304,8 @@ static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip,
spin_unlock_irqrestore
(
&
samsung_pwm_lock
,
flags
);
}
static
int
pwm_samsung_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
static
int
__
pwm_samsung_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
,
bool
force_period
)
{
struct
samsung_pwm_chip
*
our_chip
=
to_samsung_pwm_chip
(
chip
);
struct
samsung_pwm_channel
*
chan
=
pwm_get_chip_data
(
pwm
);
...
...
@@ -312,9 +319,6 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
if
(
period_ns
>
NSEC_PER_SEC
)
return
-
ERANGE
;
if
(
period_ns
==
chan
->
period_ns
&&
duty_ns
==
chan
->
duty_ns
)
return
0
;
tcnt
=
readl
(
our_chip
->
base
+
REG_TCNTB
(
pwm
->
hwpwm
));
oldtcmp
=
readl
(
our_chip
->
base
+
REG_TCMPB
(
pwm
->
hwpwm
));
...
...
@@ -322,7 +326,7 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
++
tcnt
;
/* Check to see if we are changing the clock rate of the PWM. */
if
(
chan
->
period_ns
!=
period_ns
)
{
if
(
chan
->
period_ns
!=
period_ns
||
force_period
)
{
unsigned
long
tin_rate
;
u32
period
;
...
...
@@ -381,6 +385,12 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
return
0
;
}
static
int
pwm_samsung_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
int
duty_ns
,
int
period_ns
)
{
return
__pwm_samsung_config
(
chip
,
pwm
,
duty_ns
,
period_ns
,
false
);
}
static
void
pwm_samsung_set_invert
(
struct
samsung_pwm_chip
*
chip
,
unsigned
int
channel
,
bool
invert
)
{
...
...
@@ -592,51 +602,41 @@ static int pwm_samsung_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
static
int
pwm_samsung_
suspend
(
struct
device
*
dev
)
static
int
pwm_samsung_
resume
(
struct
device
*
dev
)
{
struct
samsung_pwm_chip
*
chip
=
dev_get_drvdata
(
dev
);
struct
samsung_pwm_chip
*
our_chip
=
dev_get_drvdata
(
dev
);
struct
pwm_chip
*
chip
=
&
our_chip
->
chip
;
unsigned
int
i
;
/*
* No one preserves these values during suspend so reset them.
* Otherwise driver leaves PWM unconfigured if same values are
* passed to pwm_config() next time.
*/
for
(
i
=
0
;
i
<
SAMSUNG_PWM_NUM
;
++
i
)
{
struct
pwm_device
*
pwm
=
&
chip
->
chip
.
pwms
[
i
];
for
(
i
=
0
;
i
<
SAMSUNG_PWM_NUM
;
i
++
)
{
struct
pwm_device
*
pwm
=
&
chip
->
pwms
[
i
];
struct
samsung_pwm_channel
*
chan
=
pwm_get_chip_data
(
pwm
);
if
(
!
chan
)
continue
;
chan
->
period_ns
=
0
;
chan
->
duty_ns
=
0
;
}
if
(
our_chip
->
variant
.
output_mask
&
BIT
(
i
))
pwm_samsung_set_invert
(
our_chip
,
i
,
our_chip
->
inverter_mask
&
BIT
(
i
));
return
0
;
}
static
int
pwm_samsung_resume
(
struct
device
*
dev
)
{
struct
samsung_pwm_chip
*
chip
=
dev_get_drvdata
(
dev
);
unsigned
int
chan
;
if
(
chan
->
period_ns
)
{
__pwm_samsung_config
(
chip
,
pwm
,
chan
->
duty_ns
,
chan
->
period_ns
,
true
);
/* needed to make PWM disable work on Odroid-XU3 */
pwm_samsung_manual_update
(
our_chip
,
pwm
);
}
/*
* Inverter setting must be preserved across suspend/resume
* as nobody really seems to configure it more than once.
*/
for
(
chan
=
0
;
chan
<
SAMSUNG_PWM_NUM
;
++
chan
)
{
if
(
chip
->
variant
.
output_mask
&
BIT
(
chan
))
pwm_samsung_set_invert
(
chip
,
chan
,
chip
->
inverter_mask
&
BIT
(
chan
));
if
(
our_chip
->
disabled_mask
&
BIT
(
i
))
pwm_samsung_disable
(
chip
,
pwm
);
else
pwm_samsung_enable
(
chip
,
pwm
);
}
return
0
;
}
#endif
static
SIMPLE_DEV_PM_OPS
(
pwm_samsung_pm_ops
,
pwm_samsung_suspend
,
pwm_samsung_resume
);
static
SIMPLE_DEV_PM_OPS
(
pwm_samsung_pm_ops
,
NULL
,
pwm_samsung_resume
);
static
struct
platform_driver
pwm_samsung_driver
=
{
.
driver
=
{
...
...
drivers/pwm/pwm-tegra.c
View file @
7755daf5
...
...
@@ -218,7 +218,7 @@ static int tegra_pwm_probe(struct platform_device *pdev)
*/
pwm
->
clk_rate
=
clk_get_rate
(
pwm
->
clk
);
pwm
->
rst
=
devm_reset_control_get
(
&
pdev
->
dev
,
"pwm"
);
pwm
->
rst
=
devm_reset_control_get
_exclusive
(
&
pdev
->
dev
,
"pwm"
);
if
(
IS_ERR
(
pwm
->
rst
))
{
ret
=
PTR_ERR
(
pwm
->
rst
);
dev_err
(
&
pdev
->
dev
,
"Reset control is not found: %d
\n
"
,
ret
);
...
...
drivers/pwm/pwm-tiecap.c
View file @
7755daf5
...
...
@@ -64,9 +64,9 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int
duty_ns
,
int
period_ns
)
{
struct
ecap_pwm_chip
*
pc
=
to_ecap_pwm_chip
(
chip
);
u32
period_cycles
,
duty_cycles
;
unsigned
long
long
c
;
unsigned
long
period_cycles
,
duty_cycles
;
unsigned
int
reg_val
;
u16
value
;
if
(
period_ns
>
NSEC_PER_SEC
)
return
-
ERANGE
;
...
...
@@ -74,7 +74,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
c
=
pc
->
clk_rate
;
c
=
c
*
period_ns
;
do_div
(
c
,
NSEC_PER_SEC
);
period_cycles
=
(
u
nsigned
long
)
c
;
period_cycles
=
(
u
32
)
c
;
if
(
period_cycles
<
1
)
{
period_cycles
=
1
;
...
...
@@ -83,17 +83,17 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
c
=
pc
->
clk_rate
;
c
=
c
*
duty_ns
;
do_div
(
c
,
NSEC_PER_SEC
);
duty_cycles
=
(
u
nsigned
long
)
c
;
duty_cycles
=
(
u
32
)
c
;
}
pm_runtime_get_sync
(
pc
->
chip
.
dev
);
reg_val
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
value
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
/* Configure APWM mode & disable sync option */
reg_val
|=
ECCTL2_APWM_MODE
|
ECCTL2_SYNC_SEL_DISA
;
value
|=
ECCTL2_APWM_MODE
|
ECCTL2_SYNC_SEL_DISA
;
writew
(
reg_val
,
pc
->
mmio_base
+
ECCTL2
);
writew
(
value
,
pc
->
mmio_base
+
ECCTL2
);
if
(
!
pwm_is_enabled
(
pwm
))
{
/* Update active registers if not running */
...
...
@@ -110,13 +110,14 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
}
if
(
!
pwm_is_enabled
(
pwm
))
{
reg_val
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
value
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
/* Disable APWM mode to put APWM output Low */
reg_val
&=
~
ECCTL2_APWM_MODE
;
writew
(
reg_val
,
pc
->
mmio_base
+
ECCTL2
);
value
&=
~
ECCTL2_APWM_MODE
;
writew
(
value
,
pc
->
mmio_base
+
ECCTL2
);
}
pm_runtime_put_sync
(
pc
->
chip
.
dev
);
return
0
;
}
...
...
@@ -124,26 +125,30 @@ static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
enum
pwm_polarity
polarity
)
{
struct
ecap_pwm_chip
*
pc
=
to_ecap_pwm_chip
(
chip
);
u
nsigned
short
reg_val
;
u
16
value
;
pm_runtime_get_sync
(
pc
->
chip
.
dev
);
reg_val
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
value
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
if
(
polarity
==
PWM_POLARITY_INVERSED
)
/* Duty cycle defines LOW period of PWM */
reg_val
|=
ECCTL2_APWM_POL_LOW
;
value
|=
ECCTL2_APWM_POL_LOW
;
else
/* Duty cycle defines HIGH period of PWM */
reg_val
&=
~
ECCTL2_APWM_POL_LOW
;
value
&=
~
ECCTL2_APWM_POL_LOW
;
writew
(
value
,
pc
->
mmio_base
+
ECCTL2
);
writew
(
reg_val
,
pc
->
mmio_base
+
ECCTL2
);
pm_runtime_put_sync
(
pc
->
chip
.
dev
);
return
0
;
}
static
int
ecap_pwm_enable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
ecap_pwm_chip
*
pc
=
to_ecap_pwm_chip
(
chip
);
u
nsigned
int
reg_val
;
u
16
value
;
/* Leave clock enabled on enabling PWM */
pm_runtime_get_sync
(
pc
->
chip
.
dev
);
...
...
@@ -152,24 +157,25 @@ static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
* Enable 'Free run Time stamp counter mode' to start counter
* and 'APWM mode' to enable APWM output
*/
reg_val
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
reg_val
|=
ECCTL2_TSCTR_FREERUN
|
ECCTL2_APWM_MODE
;
writew
(
reg_val
,
pc
->
mmio_base
+
ECCTL2
);
value
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
value
|=
ECCTL2_TSCTR_FREERUN
|
ECCTL2_APWM_MODE
;
writew
(
value
,
pc
->
mmio_base
+
ECCTL2
);
return
0
;
}
static
void
ecap_pwm_disable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
ecap_pwm_chip
*
pc
=
to_ecap_pwm_chip
(
chip
);
u
nsigned
int
reg_val
;
u
16
value
;
/*
* Disable 'Free run Time stamp counter mode' to stop counter
* and 'APWM mode' to put APWM output to low
*/
reg_val
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
reg_val
&=
~
(
ECCTL2_TSCTR_FREERUN
|
ECCTL2_APWM_MODE
);
writew
(
reg_val
,
pc
->
mmio_base
+
ECCTL2
);
value
=
readw
(
pc
->
mmio_base
+
ECCTL2
);
value
&=
~
(
ECCTL2_TSCTR_FREERUN
|
ECCTL2_APWM_MODE
);
writew
(
value
,
pc
->
mmio_base
+
ECCTL2
);
/* Disable clock on PWM disable */
pm_runtime_put_sync
(
pc
->
chip
.
dev
);
...
...
@@ -202,10 +208,10 @@ MODULE_DEVICE_TABLE(of, ecap_of_match);
static
int
ecap_pwm_probe
(
struct
platform_device
*
pdev
)
{
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
int
ret
;
struct
ecap_pwm_chip
*
pc
;
struct
resource
*
r
;
struct
clk
*
clk
;
struct
ecap_pwm_chip
*
pc
;
int
ret
;
pc
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
pc
),
GFP_KERNEL
);
if
(
!
pc
)
...
...
@@ -248,9 +254,9 @@ static int ecap_pwm_probe(struct platform_device *pdev)
return
ret
;
}
platform_set_drvdata
(
pdev
,
pc
);
pm_runtime_enable
(
&
pdev
->
dev
);
platform_set_drvdata
(
pdev
,
pc
);
return
0
;
}
...
...
@@ -259,6 +265,7 @@ static int ecap_pwm_remove(struct platform_device *pdev)
struct
ecap_pwm_chip
*
pc
=
platform_get_drvdata
(
pdev
);
pm_runtime_disable
(
&
pdev
->
dev
);
return
pwmchip_remove
(
&
pc
->
chip
);
}
...
...
@@ -318,7 +325,6 @@ static struct platform_driver ecap_pwm_driver = {
.
probe
=
ecap_pwm_probe
,
.
remove
=
ecap_pwm_remove
,
};
module_platform_driver
(
ecap_pwm_driver
);
MODULE_DESCRIPTION
(
"ECAP PWM driver"
);
...
...
drivers/pwm/pwm-tiehrpwm.c
View file @
7755daf5
...
...
@@ -123,7 +123,7 @@ struct ehrpwm_context {
struct
ehrpwm_pwm_chip
{
struct
pwm_chip
chip
;
unsigned
int
clk_rate
;
unsigned
long
clk_rate
;
void
__iomem
*
mmio_base
;
unsigned
long
period_cycles
[
NUM_PWM_CHANNEL
];
enum
pwm_polarity
polarity
[
NUM_PWM_CHANNEL
];
...
...
@@ -136,25 +136,26 @@ static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
return
container_of
(
chip
,
struct
ehrpwm_pwm_chip
,
chip
);
}
static
inline
u16
ehrpwm_read
(
void
__iomem
*
base
,
int
offset
)
static
inline
u16
ehrpwm_read
(
void
__iomem
*
base
,
unsigned
int
offset
)
{
return
readw
(
base
+
offset
);
}
static
inline
void
ehrpwm_write
(
void
__iomem
*
base
,
int
offset
,
unsigned
int
val
)
static
inline
void
ehrpwm_write
(
void
__iomem
*
base
,
unsigned
int
offset
,
u16
value
)
{
writew
(
val
&
0xFFFF
,
base
+
offset
);
writew
(
val
ue
,
base
+
offset
);
}
static
void
ehrpwm_modify
(
void
__iomem
*
base
,
int
offset
,
unsigned
short
mask
,
unsigned
short
val
)
static
void
ehrpwm_modify
(
void
__iomem
*
base
,
unsigned
int
offset
,
u16
mask
,
u16
value
)
{
unsigned
short
reg
val
;
unsigned
short
val
;
reg
val
=
readw
(
base
+
offset
);
reg
val
&=
~
mask
;
regval
|=
val
&
mask
;
writew
(
reg
val
,
base
+
offset
);
val
=
readw
(
base
+
offset
);
val
&=
~
mask
;
val
|=
value
&
mask
;
writew
(
val
,
base
+
offset
);
}
/**
...
...
@@ -163,14 +164,13 @@ static void ehrpwm_modify(void __iomem *base, int offset,
* @prescale_div: prescaler value set
* @tb_clk_div: Time Base Control prescaler bits
*/
static
int
set_prescale_div
(
unsigned
long
rqst_prescaler
,
unsigned
short
*
prescale_div
,
unsigned
short
*
tb_clk_div
)
static
int
set_prescale_div
(
unsigned
long
rqst_prescaler
,
u16
*
prescale_div
,
u16
*
tb_clk_div
)
{
unsigned
int
clkdiv
,
hspclkdiv
;
for
(
clkdiv
=
0
;
clkdiv
<=
CLKDIV_MAX
;
clkdiv
++
)
{
for
(
hspclkdiv
=
0
;
hspclkdiv
<=
HSPCLKDIV_MAX
;
hspclkdiv
++
)
{
/*
* calculations for prescaler value :
* prescale_div = HSPCLKDIVIDER * CLKDIVIDER.
...
...
@@ -191,13 +191,14 @@ static int set_prescale_div(unsigned long rqst_prescaler,
}
}
}
return
1
;
}
static
void
configure_polarity
(
struct
ehrpwm_pwm_chip
*
pc
,
int
chan
)
{
int
aqctl_reg
;
unsigned
short
aqctl_val
,
aqctl_mask
;
u16
aqctl_val
,
aqctl_mask
;
unsigned
int
aqctl_reg
;
/*
* Configure PWM output to HIGH/LOW level on counter
...
...
@@ -235,10 +236,10 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int
duty_ns
,
int
period_ns
)
{
struct
ehrpwm_pwm_chip
*
pc
=
to_ehrpwm_pwm_chip
(
chip
);
u32
period_cycles
,
duty_cycles
;
u16
ps_divval
,
tb_divval
;
unsigned
int
i
,
cmp_reg
;
unsigned
long
long
c
;
unsigned
long
period_cycles
,
duty_cycles
;
unsigned
short
ps_divval
,
tb_divval
;
int
i
,
cmp_reg
;
if
(
period_ns
>
NSEC_PER_SEC
)
return
-
ERANGE
;
...
...
@@ -272,7 +273,8 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
if
(
i
==
pwm
->
hwpwm
)
continue
;
dev_err
(
chip
->
dev
,
"Period value conflicts with channel %d
\n
"
,
dev_err
(
chip
->
dev
,
"period value conflicts with channel %u
\n
"
,
i
);
return
-
EINVAL
;
}
...
...
@@ -315,23 +317,26 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
ehrpwm_write
(
pc
->
mmio_base
,
cmp_reg
,
duty_cycles
);
pm_runtime_put_sync
(
chip
->
dev
);
return
0
;
}
static
int
ehrpwm_pwm_set_polarity
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
enum
pwm_polarity
polarity
)
struct
pwm_device
*
pwm
,
enum
pwm_polarity
polarity
)
{
struct
ehrpwm_pwm_chip
*
pc
=
to_ehrpwm_pwm_chip
(
chip
);
/* Configuration of polarity in hardware delayed, do at enable */
pc
->
polarity
[
pwm
->
hwpwm
]
=
polarity
;
return
0
;
}
static
int
ehrpwm_pwm_enable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
ehrpwm_pwm_chip
*
pc
=
to_ehrpwm_pwm_chip
(
chip
);
u
nsigned
short
aqcsfrc_val
,
aqcsfrc_mask
;
u
16
aqcsfrc_val
,
aqcsfrc_mask
;
int
ret
;
/* Leave clock enabled on enabling PWM */
...
...
@@ -358,20 +363,21 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
/* Enable TBCLK before enabling PWM device */
ret
=
clk_enable
(
pc
->
tbclk
);
if
(
ret
)
{
dev_err
(
chip
->
dev
,
"Failed to enable TBCLK for %s
\n
"
,
dev_name
(
pc
->
chip
.
dev
));
dev_err
(
chip
->
dev
,
"Failed to enable TBCLK for %s
: %d
\n
"
,
dev_name
(
pc
->
chip
.
dev
)
,
ret
);
return
ret
;
}
/* Enable time counter for free_run */
ehrpwm_modify
(
pc
->
mmio_base
,
TBCTL
,
TBCTL_RUN_MASK
,
TBCTL_FREE_RUN
);
return
0
;
}
static
void
ehrpwm_pwm_disable
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
)
{
struct
ehrpwm_pwm_chip
*
pc
=
to_ehrpwm_pwm_chip
(
chip
);
u
nsigned
short
aqcsfrc_val
,
aqcsfrc_mask
;
u
16
aqcsfrc_val
,
aqcsfrc_mask
;
/* Action Qualifier puts PWM output low forcefully */
if
(
pwm
->
hwpwm
)
{
...
...
@@ -433,10 +439,10 @@ MODULE_DEVICE_TABLE(of, ehrpwm_of_match);
static
int
ehrpwm_pwm_probe
(
struct
platform_device
*
pdev
)
{
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
int
ret
;
struct
ehrpwm_pwm_chip
*
pc
;
struct
resource
*
r
;
struct
clk
*
clk
;
struct
ehrpwm_pwm_chip
*
pc
;
int
ret
;
pc
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
pc
),
GFP_KERNEL
);
if
(
!
pc
)
...
...
@@ -489,13 +495,18 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
ret
=
pwmchip_add
(
&
pc
->
chip
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"pwmchip_add() failed: %d
\n
"
,
ret
);
return
ret
;
goto
err_clk_unprepare
;
}
platform_set_drvdata
(
pdev
,
pc
);
pm_runtime_enable
(
&
pdev
->
dev
);
platform_set_drvdata
(
pdev
,
pc
);
return
0
;
err_clk_unprepare:
clk_unprepare
(
pc
->
tbclk
);
return
ret
;
}
static
int
ehrpwm_pwm_remove
(
struct
platform_device
*
pdev
)
...
...
@@ -504,8 +515,8 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev)
clk_unprepare
(
pc
->
tbclk
);
pm_runtime_put_sync
(
&
pdev
->
dev
);
pm_runtime_disable
(
&
pdev
->
dev
);
return
pwmchip_remove
(
&
pc
->
chip
);
}
...
...
@@ -513,6 +524,7 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev)
static
void
ehrpwm_pwm_save_context
(
struct
ehrpwm_pwm_chip
*
pc
)
{
pm_runtime_get_sync
(
pc
->
chip
.
dev
);
pc
->
ctx
.
tbctl
=
ehrpwm_read
(
pc
->
mmio_base
,
TBCTL
);
pc
->
ctx
.
tbprd
=
ehrpwm_read
(
pc
->
mmio_base
,
TBPRD
);
pc
->
ctx
.
cmpa
=
ehrpwm_read
(
pc
->
mmio_base
,
CMPA
);
...
...
@@ -521,6 +533,7 @@ static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc)
pc
->
ctx
.
aqctlb
=
ehrpwm_read
(
pc
->
mmio_base
,
AQCTLB
);
pc
->
ctx
.
aqsfrc
=
ehrpwm_read
(
pc
->
mmio_base
,
AQSFRC
);
pc
->
ctx
.
aqcsfrc
=
ehrpwm_read
(
pc
->
mmio_base
,
AQCSFRC
);
pm_runtime_put_sync
(
pc
->
chip
.
dev
);
}
...
...
@@ -539,9 +552,10 @@ static void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc)
static
int
ehrpwm_pwm_suspend
(
struct
device
*
dev
)
{
struct
ehrpwm_pwm_chip
*
pc
=
dev_get_drvdata
(
dev
);
int
i
;
unsigned
int
i
;
ehrpwm_pwm_save_context
(
pc
);
for
(
i
=
0
;
i
<
pc
->
chip
.
npwm
;
i
++
)
{
struct
pwm_device
*
pwm
=
&
pc
->
chip
.
pwms
[
i
];
...
...
@@ -551,13 +565,14 @@ static int ehrpwm_pwm_suspend(struct device *dev)
/* Disable explicitly if PWM is running */
pm_runtime_put_sync
(
dev
);
}
return
0
;
}
static
int
ehrpwm_pwm_resume
(
struct
device
*
dev
)
{
struct
ehrpwm_pwm_chip
*
pc
=
dev_get_drvdata
(
dev
);
int
i
;
unsigned
int
i
;
for
(
i
=
0
;
i
<
pc
->
chip
.
npwm
;
i
++
)
{
struct
pwm_device
*
pwm
=
&
pc
->
chip
.
pwms
[
i
];
...
...
@@ -568,7 +583,9 @@ static int ehrpwm_pwm_resume(struct device *dev)
/* Enable explicitly if PWM was running */
pm_runtime_get_sync
(
dev
);
}
ehrpwm_pwm_restore_context
(
pc
);
return
0
;
}
#endif
...
...
@@ -585,7 +602,6 @@ static struct platform_driver ehrpwm_pwm_driver = {
.
probe
=
ehrpwm_pwm_probe
,
.
remove
=
ehrpwm_pwm_remove
,
};
module_platform_driver
(
ehrpwm_pwm_driver
);
MODULE_DESCRIPTION
(
"EHRPWM PWM driver"
);
...
...
drivers/pwm/pwm-vt8500.c
View file @
7755daf5
...
...
@@ -241,6 +241,7 @@ static int vt8500_pwm_probe(struct platform_device *pdev)
ret
=
pwmchip_add
(
&
chip
->
chip
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"failed to add PWM chip
\n
"
);
clk_unprepare
(
chip
->
clk
);
return
ret
;
}
...
...
drivers/pwm/pwm-zx.c
0 → 100644
View file @
7755daf5
/*
* Copyright (C) 2017 Sanechips Technology Co., Ltd.
* Copyright 2017 Linaro Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#define ZX_PWM_MODE 0x0
#define ZX_PWM_CLKDIV_SHIFT 2
#define ZX_PWM_CLKDIV_MASK GENMASK(11, 2)
#define ZX_PWM_CLKDIV(x) (((x) << ZX_PWM_CLKDIV_SHIFT) & \
ZX_PWM_CLKDIV_MASK)
#define ZX_PWM_POLAR BIT(1)
#define ZX_PWM_EN BIT(0)
#define ZX_PWM_PERIOD 0x4
#define ZX_PWM_DUTY 0x8
#define ZX_PWM_CLKDIV_MAX 1023
#define ZX_PWM_PERIOD_MAX 65535
struct
zx_pwm_chip
{
struct
pwm_chip
chip
;
struct
clk
*
pclk
;
struct
clk
*
wclk
;
void
__iomem
*
base
;
};
static
inline
struct
zx_pwm_chip
*
to_zx_pwm_chip
(
struct
pwm_chip
*
chip
)
{
return
container_of
(
chip
,
struct
zx_pwm_chip
,
chip
);
}
static
inline
u32
zx_pwm_readl
(
struct
zx_pwm_chip
*
zpc
,
unsigned
int
hwpwm
,
unsigned
int
offset
)
{
return
readl
(
zpc
->
base
+
(
hwpwm
+
1
)
*
0x10
+
offset
);
}
static
inline
void
zx_pwm_writel
(
struct
zx_pwm_chip
*
zpc
,
unsigned
int
hwpwm
,
unsigned
int
offset
,
u32
value
)
{
writel
(
value
,
zpc
->
base
+
(
hwpwm
+
1
)
*
0x10
+
offset
);
}
static
void
zx_pwm_set_mask
(
struct
zx_pwm_chip
*
zpc
,
unsigned
int
hwpwm
,
unsigned
int
offset
,
u32
mask
,
u32
value
)
{
u32
data
;
data
=
zx_pwm_readl
(
zpc
,
hwpwm
,
offset
);
data
&=
~
mask
;
data
|=
value
&
mask
;
zx_pwm_writel
(
zpc
,
hwpwm
,
offset
,
data
);
}
static
void
zx_pwm_get_state
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
zx_pwm_chip
*
zpc
=
to_zx_pwm_chip
(
chip
);
unsigned
long
rate
;
unsigned
int
div
;
u32
value
;
u64
tmp
;
value
=
zx_pwm_readl
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_MODE
);
if
(
value
&
ZX_PWM_POLAR
)
state
->
polarity
=
PWM_POLARITY_NORMAL
;
else
state
->
polarity
=
PWM_POLARITY_INVERSED
;
if
(
value
&
ZX_PWM_EN
)
state
->
enabled
=
true
;
else
state
->
enabled
=
false
;
div
=
(
value
&
ZX_PWM_CLKDIV_MASK
)
>>
ZX_PWM_CLKDIV_SHIFT
;
rate
=
clk_get_rate
(
zpc
->
wclk
);
tmp
=
zx_pwm_readl
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_PERIOD
);
tmp
*=
div
*
NSEC_PER_SEC
;
state
->
period
=
DIV_ROUND_CLOSEST_ULL
(
tmp
,
rate
);
tmp
=
zx_pwm_readl
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_DUTY
);
tmp
*=
div
*
NSEC_PER_SEC
;
state
->
duty_cycle
=
DIV_ROUND_CLOSEST_ULL
(
tmp
,
rate
);
}
static
int
zx_pwm_config
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
unsigned
int
duty_ns
,
unsigned
int
period_ns
)
{
struct
zx_pwm_chip
*
zpc
=
to_zx_pwm_chip
(
chip
);
unsigned
int
period_cycles
,
duty_cycles
;
unsigned
long
long
c
;
unsigned
int
div
=
1
;
unsigned
long
rate
;
/* Find out the best divider */
rate
=
clk_get_rate
(
zpc
->
wclk
);
while
(
1
)
{
c
=
rate
/
div
;
c
=
c
*
period_ns
;
do_div
(
c
,
NSEC_PER_SEC
);
if
(
c
<
ZX_PWM_PERIOD_MAX
)
break
;
div
++
;
if
(
div
>
ZX_PWM_CLKDIV_MAX
)
return
-
ERANGE
;
}
/* Calculate duty cycles */
period_cycles
=
c
;
c
*=
duty_ns
;
do_div
(
c
,
period_ns
);
duty_cycles
=
c
;
/*
* If the PWM is being enabled, we have to temporarily disable it
* before configuring the registers.
*/
if
(
pwm_is_enabled
(
pwm
))
zx_pwm_set_mask
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_MODE
,
ZX_PWM_EN
,
0
);
/* Set up registers */
zx_pwm_set_mask
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_MODE
,
ZX_PWM_CLKDIV_MASK
,
ZX_PWM_CLKDIV
(
div
));
zx_pwm_writel
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_PERIOD
,
period_cycles
);
zx_pwm_writel
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_DUTY
,
duty_cycles
);
/* Re-enable the PWM if needed */
if
(
pwm_is_enabled
(
pwm
))
zx_pwm_set_mask
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_MODE
,
ZX_PWM_EN
,
ZX_PWM_EN
);
return
0
;
}
static
int
zx_pwm_apply
(
struct
pwm_chip
*
chip
,
struct
pwm_device
*
pwm
,
struct
pwm_state
*
state
)
{
struct
zx_pwm_chip
*
zpc
=
to_zx_pwm_chip
(
chip
);
struct
pwm_state
cstate
;
int
ret
;
pwm_get_state
(
pwm
,
&
cstate
);
if
(
state
->
polarity
!=
cstate
.
polarity
)
zx_pwm_set_mask
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_MODE
,
ZX_PWM_POLAR
,
(
state
->
polarity
==
PWM_POLARITY_INVERSED
)
?
0
:
ZX_PWM_POLAR
);
if
(
state
->
period
!=
cstate
.
period
||
state
->
duty_cycle
!=
cstate
.
duty_cycle
)
{
ret
=
zx_pwm_config
(
chip
,
pwm
,
state
->
duty_cycle
,
state
->
period
);
if
(
ret
)
return
ret
;
}
if
(
state
->
enabled
!=
cstate
.
enabled
)
{
if
(
state
->
enabled
)
{
ret
=
clk_prepare_enable
(
zpc
->
wclk
);
if
(
ret
)
return
ret
;
zx_pwm_set_mask
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_MODE
,
ZX_PWM_EN
,
ZX_PWM_EN
);
}
else
{
zx_pwm_set_mask
(
zpc
,
pwm
->
hwpwm
,
ZX_PWM_MODE
,
ZX_PWM_EN
,
0
);
clk_disable_unprepare
(
zpc
->
wclk
);
}
}
return
0
;
}
static
const
struct
pwm_ops
zx_pwm_ops
=
{
.
apply
=
zx_pwm_apply
,
.
get_state
=
zx_pwm_get_state
,
.
owner
=
THIS_MODULE
,
};
static
int
zx_pwm_probe
(
struct
platform_device
*
pdev
)
{
struct
zx_pwm_chip
*
zpc
;
struct
resource
*
res
;
unsigned
int
i
;
int
ret
;
zpc
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
zpc
),
GFP_KERNEL
);
if
(
!
zpc
)
return
-
ENOMEM
;
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
zpc
->
base
=
devm_ioremap_resource
(
&
pdev
->
dev
,
res
);
if
(
IS_ERR
(
zpc
->
base
))
return
PTR_ERR
(
zpc
->
base
);
zpc
->
pclk
=
devm_clk_get
(
&
pdev
->
dev
,
"pclk"
);
if
(
IS_ERR
(
zpc
->
pclk
))
return
PTR_ERR
(
zpc
->
pclk
);
zpc
->
wclk
=
devm_clk_get
(
&
pdev
->
dev
,
"wclk"
);
if
(
IS_ERR
(
zpc
->
wclk
))
return
PTR_ERR
(
zpc
->
wclk
);
ret
=
clk_prepare_enable
(
zpc
->
pclk
);
if
(
ret
)
return
ret
;
zpc
->
chip
.
dev
=
&
pdev
->
dev
;
zpc
->
chip
.
ops
=
&
zx_pwm_ops
;
zpc
->
chip
.
base
=
-
1
;
zpc
->
chip
.
npwm
=
4
;
zpc
->
chip
.
of_xlate
=
of_pwm_xlate_with_flags
;
zpc
->
chip
.
of_pwm_n_cells
=
3
;
/*
* PWM devices may be enabled by firmware, and let's disable all of
* them initially to save power.
*/
for
(
i
=
0
;
i
<
zpc
->
chip
.
npwm
;
i
++
)
zx_pwm_set_mask
(
zpc
,
i
,
ZX_PWM_MODE
,
ZX_PWM_EN
,
0
);
ret
=
pwmchip_add
(
&
zpc
->
chip
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"failed to add PWM chip: %d
\n
"
,
ret
);
return
ret
;
}
platform_set_drvdata
(
pdev
,
zpc
);
return
0
;
}
static
int
zx_pwm_remove
(
struct
platform_device
*
pdev
)
{
struct
zx_pwm_chip
*
zpc
=
platform_get_drvdata
(
pdev
);
int
ret
;
ret
=
pwmchip_remove
(
&
zpc
->
chip
);
clk_disable_unprepare
(
zpc
->
pclk
);
return
ret
;
}
static
const
struct
of_device_id
zx_pwm_dt_ids
[]
=
{
{
.
compatible
=
"zte,zx296718-pwm"
,
},
{
/* sentinel */
}
};
MODULE_DEVICE_TABLE
(
of
,
zx_pwm_dt_ids
);
static
struct
platform_driver
zx_pwm_driver
=
{
.
driver
=
{
.
name
=
"zx-pwm"
,
.
of_match_table
=
zx_pwm_dt_ids
,
},
.
probe
=
zx_pwm_probe
,
.
remove
=
zx_pwm_remove
,
};
module_platform_driver
(
zx_pwm_driver
);
MODULE_ALIAS
(
"platform:zx-pwm"
);
MODULE_AUTHOR
(
"Shawn Guo <shawn.guo@linaro.org>"
);
MODULE_DESCRIPTION
(
"ZTE ZX PWM Driver"
);
MODULE_LICENSE
(
"GPL v2"
);
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