Commit 66c9457d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pwm/for-4.14-rc1' of...

Merge tag 'pwm/for-4.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm

Pull pwm updates from Thierry Reding:
 "The changes for this release include a new driver for the PWM
  controller found on SoCs of the ZTX ZX family. Support for an old
  SH-Mobile SoC has been dropped and the Rockchip and MediaTek drivers
  gain support for more generations.

  Other than that there are a bunch of coding style fixes, minor bug
  fixes and cleanup as well as documentation patches"

* tag 'pwm/for-4.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (32 commits)
  pwm: pwm-samsung: fix suspend/resume support
  pwm: samsung: Remove redundant checks from pwm_samsung_config()
  pwm: mediatek: Disable clock on PWM configuration failure
  dt-bindings: pwm: Add MT2712/MT7622 information
  pwm: mediatek: Fix clock control issue
  pwm: mediatek: Fix PWM source clock selection
  pwm: mediatek: Fix Kconfig description
  pwm: tegra: Explicitly request exclusive reset control
  pwm: hibvt: Explicitly request exclusive reset control
  pwm: tiehrpwm: Set driver data before runtime PM enable
  pwm: tiehrpwm: Miscellaneous coding style fixups
  pwm: tiecap: Set driver data before runtime PM enable
  pwm: tiecap: Miscellaneous coding style fixups
  dt-bindings: pwm: tiecap: Add TI 66AK2G SoC specific compatible
  pwm: tiehrpwm: fix clock imbalance in probe error path
  pwm: tiehrpwm: Fix runtime PM imbalance at unbind
  pwm: Kconfig: Enable pwm-tiecap to be built for Keystone
  pwm: Add ZTE ZX PWM device driver
  dt-bindings: pwm: Add bindings doc for ZTE ZX PWM controller
  pwm: bcm2835: Support for polarity setting via DT
  ...
parents 669bf77a 7755daf5
...@@ -6,7 +6,7 @@ Required properties: ...@@ -6,7 +6,7 @@ Required properties:
- clocks: This clock defines the base clock frequency of the PWM hardware - 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 system, the period and the duty_cycle of the PWM signal is a multiple of
the base period. 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. the cells format.
Examples: Examples:
...@@ -15,7 +15,7 @@ pwm@2020c000 { ...@@ -15,7 +15,7 @@ pwm@2020c000 {
compatible = "brcm,bcm2835-pwm"; compatible = "brcm,bcm2835-pwm";
reg = <0x2020c000 0x28>; reg = <0x2020c000 0x28>;
clocks = <&clk_pwm>; clocks = <&clk_pwm>;
#pwm-cells = <2>; #pwm-cells = <3>;
}; };
clocks { clocks {
......
...@@ -2,6 +2,8 @@ MediaTek PWM controller ...@@ -2,6 +2,8 @@ MediaTek PWM controller
Required properties: Required properties:
- compatible: should be "mediatek,<name>-pwm": - 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. - "mediatek,mt7623-pwm": found on mt7623 SoC.
- reg: physical base address and length of the controller's registers. - 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 - #pwm-cells: must be 2. See pwm.txt in this directory for a description of
...@@ -10,7 +12,9 @@ Required properties: ...@@ -10,7 +12,9 @@ Required properties:
- clock-names: must contain the following: - clock-names: must contain the following:
- "top": the top clock generator - "top": the top clock generator
- "main": clock used by the PWM core - "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-names: Must contain a "default" entry.
- pinctrl-0: One property must exist for each entry in pinctrl-names. - pinctrl-0: One property must exist for each entry in pinctrl-names.
See pinctrl/pinctrl-bindings.txt for details of the property values. See pinctrl/pinctrl-bindings.txt for details of the property values.
......
...@@ -3,10 +3,17 @@ Rockchip PWM controller ...@@ -3,10 +3,17 @@ Rockchip PWM controller
Required properties: Required properties:
- compatible: should be "rockchip,<name>-pwm" - compatible: should be "rockchip,<name>-pwm"
"rockchip,rk2928-pwm": found on RK29XX,RK3066 and RK3188 SoCs "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 "rockchip,vop-pwm": found integrated in VOP on RK3288 SoC
- reg: physical base address and length of the controller's registers - 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 - #pwm-cells: must be 2 (rk2928) or 3 (rk3288). See pwm.txt in this directory
for a description of the cell format. for a description of the cell format.
......
...@@ -6,6 +6,7 @@ Required properties: ...@@ -6,6 +6,7 @@ Required properties:
for am4372 - compatible = "ti,am4372-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; 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 da850 - compatible = "ti,da850-ecap", "ti,am3352-ecap", "ti,am33xx-ecap";
for dra746 - compatible = "ti,dra746-ecap", "ti,am3352-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 - #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 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. cell flag supported by this binding is PWM_POLARITY_INVERTED.
......
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>;
};
...@@ -6,7 +6,6 @@ Required Properties: ...@@ -6,7 +6,6 @@ Required Properties:
- "renesas,tpu-r8a73a4": for R8A77A4 (R-Mobile APE6) compatible PWM controller. - "renesas,tpu-r8a73a4": for R8A77A4 (R-Mobile APE6) compatible PWM controller.
- "renesas,tpu-r8a7740": for R8A7740 (R-Mobile A1) 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-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. - "renesas,tpu": for generic R-Car TPU PWM controller.
- reg: Base address and length of each memory resource used by the PWM - reg: Base address and length of each memory resource used by the PWM
......
...@@ -300,7 +300,7 @@ config PWM_MEDIATEK ...@@ -300,7 +300,7 @@ config PWM_MEDIATEK
Generic PWM framework driver for Mediatek ARM SoC. Generic PWM framework driver for Mediatek ARM SoC.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pwm-mxs. will be called pwm-mediatek.
config PWM_MXS config PWM_MXS
tristate "Freescale MXS PWM support" tristate "Freescale MXS PWM support"
...@@ -456,7 +456,7 @@ config PWM_TEGRA ...@@ -456,7 +456,7 @@ config PWM_TEGRA
config PWM_TIECAP config PWM_TIECAP
tristate "ECAP PWM support" tristate "ECAP PWM support"
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE
help help
PWM driver support for the ECAP APWM controller found on AM33XX PWM driver support for the ECAP APWM controller found on AM33XX
TI SOC TI SOC
...@@ -510,4 +510,13 @@ config PWM_VT8500 ...@@ -510,4 +510,13 @@ config PWM_VT8500
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pwm-vt8500. 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 endif
...@@ -50,3 +50,4 @@ obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o ...@@ -50,3 +50,4 @@ obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o
obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o
obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o
obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
obj-$(CONFIG_PWM_ZX) += pwm-zx.o
...@@ -167,6 +167,8 @@ static int bcm2835_pwm_probe(struct platform_device *pdev) ...@@ -167,6 +167,8 @@ static int bcm2835_pwm_probe(struct platform_device *pdev)
pc->chip.dev = &pdev->dev; pc->chip.dev = &pdev->dev;
pc->chip.ops = &bcm2835_pwm_ops; pc->chip.ops = &bcm2835_pwm_ops;
pc->chip.npwm = 2; 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); platform_set_drvdata(pdev, pc);
......
...@@ -208,7 +208,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev) ...@@ -208,7 +208,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
return ret; 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)) { if (IS_ERR(pwm_chip->rstc)) {
clk_disable_unprepare(pwm_chip->clk); clk_disable_unprepare(pwm_chip->clk);
return PTR_ERR(pwm_chip->rstc); return PTR_ERR(pwm_chip->rstc);
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Mediatek Pulse Width Modulator driver * Mediatek Pulse Width Modulator driver
* *
* Copyright (C) 2015 John Crispin <blogic@openwrt.org> * 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 * This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any * License version 2. This program is licensed "as is" without any
...@@ -29,6 +30,8 @@ ...@@ -29,6 +30,8 @@
#define PWMDWIDTH 0x2c #define PWMDWIDTH 0x2c
#define PWMTHRES 0x30 #define PWMTHRES 0x30
#define PWM_CLK_DIV_MAX 7
enum { enum {
MTK_CLK_MAIN = 0, MTK_CLK_MAIN = 0,
MTK_CLK_TOP, MTK_CLK_TOP,
...@@ -61,6 +64,42 @@ static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip) ...@@ -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); 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, static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num,
unsigned int offset) unsigned int offset)
{ {
...@@ -80,6 +119,11 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -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 mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]; struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm];
u32 resolution, clkdiv = 0; 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); 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, ...@@ -88,13 +132,18 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
clkdiv++; 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; 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, PWMDWIDTH, period_ns / resolution);
mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution); mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution);
mtk_pwm_clk_disable(chip, pwm);
return 0; return 0;
} }
...@@ -104,7 +153,7 @@ static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -104,7 +153,7 @@ static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
u32 value; u32 value;
int ret; int ret;
ret = clk_prepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); ret = mtk_pwm_clk_enable(chip, pwm);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -124,7 +173,7 @@ static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -124,7 +173,7 @@ static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
value &= ~BIT(pwm->hwpwm); value &= ~BIT(pwm->hwpwm);
writel(value, pc->regs); 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 = { static const struct pwm_ops mtk_pwm_ops = {
...@@ -156,14 +205,6 @@ static int mtk_pwm_probe(struct platform_device *pdev) ...@@ -156,14 +205,6 @@ static int mtk_pwm_probe(struct platform_device *pdev)
return PTR_ERR(pc->clks[i]); return PTR_ERR(pc->clks[i]);
} }
ret = clk_prepare(pc->clks[MTK_CLK_TOP]);
if (ret < 0)
return ret;
ret = clk_prepare(pc->clks[MTK_CLK_MAIN]);
if (ret < 0)
goto disable_clk_top;
platform_set_drvdata(pdev, pc); platform_set_drvdata(pdev, pc);
pc->chip.dev = &pdev->dev; pc->chip.dev = &pdev->dev;
...@@ -174,26 +215,15 @@ static int mtk_pwm_probe(struct platform_device *pdev) ...@@ -174,26 +215,15 @@ static int mtk_pwm_probe(struct platform_device *pdev)
ret = pwmchip_add(&pc->chip); ret = pwmchip_add(&pc->chip);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
goto disable_clk_main; return ret;
} }
return 0; 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) static int mtk_pwm_remove(struct platform_device *pdev)
{ {
struct mtk_pwm_chip *pc = platform_get_drvdata(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); return pwmchip_remove(&pc->chip);
} }
......
...@@ -441,7 +441,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson, ...@@ -441,7 +441,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson,
for (i = 0; i < meson->chip.npwm; i++) { for (i = 0; i < meson->chip.npwm; i++) {
struct meson_pwm_channel *channel = &channels[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.name = name;
init.ops = &clk_mux_ops; init.ops = &clk_mux_ops;
......
...@@ -241,11 +241,11 @@ static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca) ...@@ -241,11 +241,11 @@ static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
} }
#endif #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, regmap_update_bits(pca->regmap, PCA9685_MODE1,
MODE1_SLEEP, sleep ? MODE1_SLEEP : 0); MODE1_SLEEP, enable ? MODE1_SLEEP : 0);
if (!sleep) { if (!enable) {
/* Wait 500us for the oscillator to be back up */ /* Wait 500us for the oscillator to be back up */
udelay(500); udelay(500);
} }
...@@ -272,13 +272,13 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -272,13 +272,13 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* state is guaranteed active here. * state is guaranteed active here.
*/ */
/* Put chip into sleep mode */ /* Put chip into sleep mode */
pca9685_set_sleep_mode(pca, 1); pca9685_set_sleep_mode(pca, true);
/* Change the chip-wide output frequency */ /* Change the chip-wide output frequency */
regmap_write(pca->regmap, PCA9685_PRESCALE, prescale); regmap_write(pca->regmap, PCA9685_PRESCALE, prescale);
/* Wake the chip up */ /* Wake the chip up */
pca9685_set_sleep_mode(pca, 0); pca9685_set_sleep_mode(pca, false);
pca->period_ns = period_ns; pca->period_ns = period_ns;
} else { } else {
...@@ -534,7 +534,7 @@ static int pca9685_pwm_runtime_suspend(struct device *dev) ...@@ -534,7 +534,7 @@ static int pca9685_pwm_runtime_suspend(struct device *dev)
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct pca9685 *pca = i2c_get_clientdata(client); struct pca9685 *pca = i2c_get_clientdata(client);
pca9685_set_sleep_mode(pca, 1); pca9685_set_sleep_mode(pca, true);
return 0; return 0;
} }
...@@ -543,7 +543,7 @@ static int pca9685_pwm_runtime_resume(struct device *dev) ...@@ -543,7 +543,7 @@ static int pca9685_pwm_runtime_resume(struct device *dev)
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct pca9685 *pca = i2c_get_clientdata(client); struct pca9685 *pca = i2c_get_clientdata(client);
pca9685_set_sleep_mode(pca, 0); pca9685_set_sleep_mode(pca, false);
return 0; return 0;
} }
#endif #endif
......
...@@ -455,7 +455,6 @@ static const struct of_device_id tpu_of_table[] = { ...@@ -455,7 +455,6 @@ static const struct of_device_id tpu_of_table[] = {
{ .compatible = "renesas,tpu-r8a73a4", }, { .compatible = "renesas,tpu-r8a73a4", },
{ .compatible = "renesas,tpu-r8a7740", }, { .compatible = "renesas,tpu-r8a7740", },
{ .compatible = "renesas,tpu-r8a7790", }, { .compatible = "renesas,tpu-r8a7790", },
{ .compatible = "renesas,tpu-sh7372", },
{ .compatible = "renesas,tpu", }, { .compatible = "renesas,tpu", },
{ }, { },
}; };
......
This diff is collapsed.
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Copyright (c) 2008 Simtec Electronics * Copyright (c) 2008 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org> * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
* Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> * Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com>
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
* *
* PWM driver for Samsung SoCs * PWM driver for Samsung SoCs
* *
...@@ -74,6 +75,7 @@ struct samsung_pwm_channel { ...@@ -74,6 +75,7 @@ struct samsung_pwm_channel {
* @chip: generic PWM chip * @chip: generic PWM chip
* @variant: local copy of hardware variant data * @variant: local copy of hardware variant data
* @inverter_mask: inverter status for all channels - one bit per channel * @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: base address of mapped PWM registers
* @base_clk: base clock used to drive the timers * @base_clk: base clock used to drive the timers
* @tclk0: external clock 0 (can be ERR_PTR if not present) * @tclk0: external clock 0 (can be ERR_PTR if not present)
...@@ -83,6 +85,7 @@ struct samsung_pwm_chip { ...@@ -83,6 +85,7 @@ struct samsung_pwm_chip {
struct pwm_chip chip; struct pwm_chip chip;
struct samsung_pwm_variant variant; struct samsung_pwm_variant variant;
u8 inverter_mask; u8 inverter_mask;
u8 disabled_mask;
void __iomem *base; void __iomem *base;
struct clk *base_clk; struct clk *base_clk;
...@@ -257,6 +260,8 @@ static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -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); tcon |= TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan);
writel(tcon, our_chip->base + REG_TCON); writel(tcon, our_chip->base + REG_TCON);
our_chip->disabled_mask &= ~BIT(pwm->hwpwm);
spin_unlock_irqrestore(&samsung_pwm_lock, flags); spin_unlock_irqrestore(&samsung_pwm_lock, flags);
return 0; return 0;
...@@ -275,6 +280,8 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -275,6 +280,8 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm)
tcon &= ~TCON_AUTORELOAD(tcon_chan); tcon &= ~TCON_AUTORELOAD(tcon_chan);
writel(tcon, our_chip->base + REG_TCON); writel(tcon, our_chip->base + REG_TCON);
our_chip->disabled_mask |= BIT(pwm->hwpwm);
spin_unlock_irqrestore(&samsung_pwm_lock, flags); spin_unlock_irqrestore(&samsung_pwm_lock, flags);
} }
...@@ -297,8 +304,8 @@ static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, ...@@ -297,8 +304,8 @@ static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip,
spin_unlock_irqrestore(&samsung_pwm_lock, flags); spin_unlock_irqrestore(&samsung_pwm_lock, flags);
} }
static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns) int duty_ns, int period_ns, bool force_period)
{ {
struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); 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, ...@@ -312,9 +319,6 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
if (period_ns > NSEC_PER_SEC) if (period_ns > NSEC_PER_SEC)
return -ERANGE; return -ERANGE;
if (period_ns == chan->period_ns && duty_ns == chan->duty_ns)
return 0;
tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm)); tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm));
oldtcmp = readl(our_chip->base + REG_TCMPB(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, ...@@ -322,7 +326,7 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
++tcnt; ++tcnt;
/* Check to see if we are changing the clock rate of the PWM. */ /* 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; unsigned long tin_rate;
u32 period; u32 period;
...@@ -381,6 +385,12 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -381,6 +385,12 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
return 0; 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, static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip,
unsigned int channel, bool invert) unsigned int channel, bool invert)
{ {
...@@ -592,51 +602,41 @@ static int pwm_samsung_remove(struct platform_device *pdev) ...@@ -592,51 +602,41 @@ static int pwm_samsung_remove(struct platform_device *pdev)
} }
#ifdef CONFIG_PM_SLEEP #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; unsigned int i;
/* for (i = 0; i < SAMSUNG_PWM_NUM; i++) {
* No one preserves these values during suspend so reset them. struct pwm_device *pwm = &chip->pwms[i];
* 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];
struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm);
if (!chan) if (!chan)
continue; continue;
chan->period_ns = 0; if (our_chip->variant.output_mask & BIT(i))
chan->duty_ns = 0; pwm_samsung_set_invert(our_chip, i,
} our_chip->inverter_mask & BIT(i));
return 0;
}
static int pwm_samsung_resume(struct device *dev) if (chan->period_ns) {
{ __pwm_samsung_config(chip, pwm, chan->duty_ns,
struct samsung_pwm_chip *chip = dev_get_drvdata(dev); chan->period_ns, true);
unsigned int chan; /* needed to make PWM disable work on Odroid-XU3 */
pwm_samsung_manual_update(our_chip, pwm);
}
/* if (our_chip->disabled_mask & BIT(i))
* Inverter setting must be preserved across suspend/resume pwm_samsung_disable(chip, pwm);
* as nobody really seems to configure it more than once. else
*/ pwm_samsung_enable(chip, pwm);
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));
} }
return 0; return 0;
} }
#endif #endif
static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, pwm_samsung_suspend, static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, NULL, pwm_samsung_resume);
pwm_samsung_resume);
static struct platform_driver pwm_samsung_driver = { static struct platform_driver pwm_samsung_driver = {
.driver = { .driver = {
......
...@@ -218,7 +218,7 @@ static int tegra_pwm_probe(struct platform_device *pdev) ...@@ -218,7 +218,7 @@ static int tegra_pwm_probe(struct platform_device *pdev)
*/ */
pwm->clk_rate = clk_get_rate(pwm->clk); 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)) { if (IS_ERR(pwm->rst)) {
ret = PTR_ERR(pwm->rst); ret = PTR_ERR(pwm->rst);
dev_err(&pdev->dev, "Reset control is not found: %d\n", ret); dev_err(&pdev->dev, "Reset control is not found: %d\n", ret);
......
...@@ -39,15 +39,15 @@ ...@@ -39,15 +39,15 @@
#define ECCTL2_TSCTR_FREERUN BIT(4) #define ECCTL2_TSCTR_FREERUN BIT(4)
struct ecap_context { struct ecap_context {
u32 cap3; u32 cap3;
u32 cap4; u32 cap4;
u16 ecctl2; u16 ecctl2;
}; };
struct ecap_pwm_chip { struct ecap_pwm_chip {
struct pwm_chip chip; struct pwm_chip chip;
unsigned int clk_rate; unsigned int clk_rate;
void __iomem *mmio_base; void __iomem *mmio_base;
struct ecap_context ctx; struct ecap_context ctx;
}; };
...@@ -64,9 +64,9 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -64,9 +64,9 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns) int duty_ns, int period_ns)
{ {
struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
u32 period_cycles, duty_cycles;
unsigned long long c; unsigned long long c;
unsigned long period_cycles, duty_cycles; u16 value;
unsigned int reg_val;
if (period_ns > NSEC_PER_SEC) if (period_ns > NSEC_PER_SEC)
return -ERANGE; return -ERANGE;
...@@ -74,7 +74,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -74,7 +74,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
c = pc->clk_rate; c = pc->clk_rate;
c = c * period_ns; c = c * period_ns;
do_div(c, NSEC_PER_SEC); do_div(c, NSEC_PER_SEC);
period_cycles = (unsigned long)c; period_cycles = (u32)c;
if (period_cycles < 1) { if (period_cycles < 1) {
period_cycles = 1; period_cycles = 1;
...@@ -83,17 +83,17 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -83,17 +83,17 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
c = pc->clk_rate; c = pc->clk_rate;
c = c * duty_ns; c = c * duty_ns;
do_div(c, NSEC_PER_SEC); do_div(c, NSEC_PER_SEC);
duty_cycles = (unsigned long)c; duty_cycles = (u32)c;
} }
pm_runtime_get_sync(pc->chip.dev); 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 */ /* 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)) { if (!pwm_is_enabled(pwm)) {
/* Update active registers if not running */ /* Update active registers if not running */
...@@ -110,40 +110,45 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -110,40 +110,45 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
} }
if (!pwm_is_enabled(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 */ /* Disable APWM mode to put APWM output Low */
reg_val &= ~ECCTL2_APWM_MODE; value &= ~ECCTL2_APWM_MODE;
writew(reg_val, pc->mmio_base + ECCTL2); writew(value, pc->mmio_base + ECCTL2);
} }
pm_runtime_put_sync(pc->chip.dev); pm_runtime_put_sync(pc->chip.dev);
return 0; return 0;
} }
static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
enum pwm_polarity polarity) enum pwm_polarity polarity)
{ {
struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
unsigned short reg_val; u16 value;
pm_runtime_get_sync(pc->chip.dev); 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) if (polarity == PWM_POLARITY_INVERSED)
/* Duty cycle defines LOW period of PWM */ /* Duty cycle defines LOW period of PWM */
reg_val |= ECCTL2_APWM_POL_LOW; value |= ECCTL2_APWM_POL_LOW;
else else
/* Duty cycle defines HIGH period of PWM */ /* 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); pm_runtime_put_sync(pc->chip.dev);
return 0; return 0;
} }
static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
unsigned int reg_val; u16 value;
/* Leave clock enabled on enabling PWM */ /* Leave clock enabled on enabling PWM */
pm_runtime_get_sync(pc->chip.dev); pm_runtime_get_sync(pc->chip.dev);
...@@ -152,24 +157,25 @@ static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -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 * Enable 'Free run Time stamp counter mode' to start counter
* and 'APWM mode' to enable APWM output * and 'APWM mode' to enable APWM output
*/ */
reg_val = readw(pc->mmio_base + ECCTL2); value = readw(pc->mmio_base + ECCTL2);
reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE; value |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
writew(reg_val, pc->mmio_base + ECCTL2); writew(value, pc->mmio_base + ECCTL2);
return 0; return 0;
} }
static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
unsigned int reg_val; u16 value;
/* /*
* Disable 'Free run Time stamp counter mode' to stop counter * Disable 'Free run Time stamp counter mode' to stop counter
* and 'APWM mode' to put APWM output to low * and 'APWM mode' to put APWM output to low
*/ */
reg_val = readw(pc->mmio_base + ECCTL2); value = readw(pc->mmio_base + ECCTL2);
reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE); value &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
writew(reg_val, pc->mmio_base + ECCTL2); writew(value, pc->mmio_base + ECCTL2);
/* Disable clock on PWM disable */ /* Disable clock on PWM disable */
pm_runtime_put_sync(pc->chip.dev); pm_runtime_put_sync(pc->chip.dev);
...@@ -184,12 +190,12 @@ static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -184,12 +190,12 @@ static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
} }
static const struct pwm_ops ecap_pwm_ops = { static const struct pwm_ops ecap_pwm_ops = {
.free = ecap_pwm_free, .free = ecap_pwm_free,
.config = ecap_pwm_config, .config = ecap_pwm_config,
.set_polarity = ecap_pwm_set_polarity, .set_polarity = ecap_pwm_set_polarity,
.enable = ecap_pwm_enable, .enable = ecap_pwm_enable,
.disable = ecap_pwm_disable, .disable = ecap_pwm_disable,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static const struct of_device_id ecap_of_match[] = { static const struct of_device_id ecap_of_match[] = {
...@@ -202,10 +208,10 @@ MODULE_DEVICE_TABLE(of, ecap_of_match); ...@@ -202,10 +208,10 @@ MODULE_DEVICE_TABLE(of, ecap_of_match);
static int ecap_pwm_probe(struct platform_device *pdev) static int ecap_pwm_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
int ret; struct ecap_pwm_chip *pc;
struct resource *r; struct resource *r;
struct clk *clk; struct clk *clk;
struct ecap_pwm_chip *pc; int ret;
pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
if (!pc) if (!pc)
...@@ -248,9 +254,9 @@ static int ecap_pwm_probe(struct platform_device *pdev) ...@@ -248,9 +254,9 @@ static int ecap_pwm_probe(struct platform_device *pdev)
return ret; return ret;
} }
platform_set_drvdata(pdev, pc);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
platform_set_drvdata(pdev, pc);
return 0; return 0;
} }
...@@ -259,6 +265,7 @@ static int ecap_pwm_remove(struct platform_device *pdev) ...@@ -259,6 +265,7 @@ static int ecap_pwm_remove(struct platform_device *pdev)
struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
return pwmchip_remove(&pc->chip); return pwmchip_remove(&pc->chip);
} }
...@@ -311,14 +318,13 @@ static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume); ...@@ -311,14 +318,13 @@ static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume);
static struct platform_driver ecap_pwm_driver = { static struct platform_driver ecap_pwm_driver = {
.driver = { .driver = {
.name = "ecap", .name = "ecap",
.of_match_table = ecap_of_match, .of_match_table = ecap_of_match,
.pm = &ecap_pwm_pm_ops, .pm = &ecap_pwm_pm_ops,
}, },
.probe = ecap_pwm_probe, .probe = ecap_pwm_probe,
.remove = ecap_pwm_remove, .remove = ecap_pwm_remove,
}; };
module_platform_driver(ecap_pwm_driver); module_platform_driver(ecap_pwm_driver);
MODULE_DESCRIPTION("ECAP PWM driver"); MODULE_DESCRIPTION("ECAP PWM driver");
......
This diff is collapsed.
...@@ -241,6 +241,7 @@ static int vt8500_pwm_probe(struct platform_device *pdev) ...@@ -241,6 +241,7 @@ static int vt8500_pwm_probe(struct platform_device *pdev)
ret = pwmchip_add(&chip->chip); ret = pwmchip_add(&chip->chip);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to add PWM chip\n"); dev_err(&pdev->dev, "failed to add PWM chip\n");
clk_unprepare(chip->clk);
return ret; return ret;
} }
......
/*
* 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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment