Commit 7755daf5 authored by Thierry Reding's avatar Thierry Reding

Merge branch 'for-4.14/drivers' into for-next

parents 5771a8c0 08a4d8ec
...@@ -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"
...@@ -446,7 +446,7 @@ config PWM_TEGRA ...@@ -446,7 +446,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
...@@ -500,4 +500,13 @@ config PWM_VT8500 ...@@ -500,4 +500,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
...@@ -49,3 +49,4 @@ obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o ...@@ -49,3 +49,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", },
{ }, { },
}; };
......
...@@ -27,12 +27,15 @@ ...@@ -27,12 +27,15 @@
#define PWM_DUTY_NEGATIVE (0 << 3) #define PWM_DUTY_NEGATIVE (0 << 3)
#define PWM_INACTIVE_NEGATIVE (0 << 4) #define PWM_INACTIVE_NEGATIVE (0 << 4)
#define PWM_INACTIVE_POSITIVE (1 << 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_OUTPUT_LEFT (0 << 5)
#define PWM_LOCK_EN (1 << 6)
#define PWM_LP_DISABLE (0 << 8) #define PWM_LP_DISABLE (0 << 8)
struct rockchip_pwm_chip { struct rockchip_pwm_chip {
struct pwm_chip chip; struct pwm_chip chip;
struct clk *clk; struct clk *clk;
struct clk *pclk;
const struct rockchip_pwm_data *data; const struct rockchip_pwm_data *data;
void __iomem *base; void __iomem *base;
}; };
...@@ -48,13 +51,8 @@ struct rockchip_pwm_data { ...@@ -48,13 +51,8 @@ struct rockchip_pwm_data {
struct rockchip_pwm_regs regs; struct rockchip_pwm_regs regs;
unsigned int prescaler; unsigned int prescaler;
bool supports_polarity; bool supports_polarity;
const struct pwm_ops *ops; bool supports_lock;
u32 enable_conf;
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);
}; };
static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) 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) ...@@ -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); 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, static void rockchip_pwm_get_state(struct pwm_chip *chip,
struct pwm_device *pwm, struct pwm_device *pwm,
struct pwm_state *state) struct pwm_state *state)
{ {
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
u32 enable_conf = pc->data->enable_conf;
unsigned long clk_rate; unsigned long clk_rate;
u64 tmp; u64 tmp;
u32 val;
int ret; int ret;
ret = clk_enable(pc->clk); ret = clk_enable(pc->pclk);
if (ret) if (ret)
return; return;
...@@ -157,19 +83,31 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, ...@@ -157,19 +83,31 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip,
tmp = readl_relaxed(pc->base + pc->data->regs.duty); tmp = readl_relaxed(pc->base + pc->data->regs.duty);
tmp *= pc->data->prescaler * NSEC_PER_SEC; tmp *= pc->data->prescaler * NSEC_PER_SEC;
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
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;
pc->data->get_state(chip, pwm, state); if (pc->data->supports_polarity) {
if (!(val & PWM_DUTY_POSITIVE))
state->polarity = PWM_POLARITY_INVERSED;
}
clk_disable(pc->clk); clk_disable(pc->pclk);
} }
static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns) struct pwm_state *state)
{ {
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
unsigned long period, duty; unsigned long period, duty;
u64 clk_rate, div; u64 clk_rate, div;
u32 ctrl;
clk_rate = clk_get_rate(pc->clk); clk_rate = clk_get_rate(pc->clk);
...@@ -178,26 +116,53 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -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 * bits, every possible input period can be obtained using the
* default prescaler value for all practical clock rate values. * 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, period = DIV_ROUND_CLOSEST_ULL(div,
pc->data->prescaler * NSEC_PER_SEC); 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); 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(period, pc->base + pc->data->regs.period);
writel(duty, pc->base + pc->data->regs.duty); 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, static int rockchip_pwm_enable(struct pwm_chip *chip,
struct pwm_device *pwm, struct pwm_device *pwm,
bool enable, bool enable)
enum pwm_polarity polarity)
{ {
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
u32 enable_conf = pc->data->enable_conf;
int ret; int ret;
u32 val;
if (enable) { if (enable) {
ret = clk_enable(pc->clk); ret = clk_enable(pc->clk);
...@@ -205,7 +170,14 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, ...@@ -205,7 +170,14 @@ static int rockchip_pwm_enable(struct pwm_chip *chip,
return ret; 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) if (!enable)
clk_disable(pc->clk); clk_disable(pc->clk);
...@@ -219,33 +191,26 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -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 rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
struct pwm_state curstate; struct pwm_state curstate;
bool enabled; bool enabled;
int ret; int ret = 0;
pwm_get_state(pwm, &curstate); ret = clk_enable(pc->pclk);
enabled = curstate.enabled;
ret = clk_enable(pc->clk);
if (ret) if (ret)
return ret; return ret;
if (state->polarity != curstate.polarity && enabled) { pwm_get_state(pwm, &curstate);
ret = rockchip_pwm_enable(chip, pwm, false, state->polarity); enabled = curstate.enabled;
if (state->polarity != curstate.polarity && enabled &&
!pc->data->supports_lock) {
ret = rockchip_pwm_enable(chip, pwm, false);
if (ret) if (ret)
goto out; goto out;
enabled = false; enabled = false;
} }
ret = rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); rockchip_pwm_config(chip, pwm, state);
if (ret) {
if (enabled != curstate.enabled)
rockchip_pwm_enable(chip, pwm, !enabled,
state->polarity);
goto out;
}
if (state->enabled != enabled) { if (state->enabled != enabled) {
ret = rockchip_pwm_enable(chip, pwm, state->enabled, ret = rockchip_pwm_enable(chip, pwm, state->enabled);
state->polarity);
if (ret) if (ret)
goto out; goto out;
} }
...@@ -257,18 +222,12 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -257,18 +222,12 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
rockchip_pwm_get_state(chip, pwm, state); rockchip_pwm_get_state(chip, pwm, state);
out: out:
clk_disable(pc->clk); clk_disable(pc->pclk);
return ret; return ret;
} }
static const struct pwm_ops rockchip_pwm_ops_v1 = { static const struct pwm_ops rockchip_pwm_ops = {
.get_state = rockchip_pwm_get_state,
.apply = rockchip_pwm_apply,
.owner = THIS_MODULE,
};
static const struct pwm_ops rockchip_pwm_ops_v2 = {
.get_state = rockchip_pwm_get_state, .get_state = rockchip_pwm_get_state,
.apply = rockchip_pwm_apply, .apply = rockchip_pwm_apply,
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -282,9 +241,9 @@ static const struct rockchip_pwm_data pwm_data_v1 = { ...@@ -282,9 +241,9 @@ static const struct rockchip_pwm_data pwm_data_v1 = {
.ctrl = 0x0c, .ctrl = 0x0c,
}, },
.prescaler = 2, .prescaler = 2,
.ops = &rockchip_pwm_ops_v1, .supports_polarity = false,
.set_enable = rockchip_pwm_set_enable_v1, .supports_lock = false,
.get_state = rockchip_pwm_get_state_v1, .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
}; };
static const struct rockchip_pwm_data pwm_data_v2 = { static const struct rockchip_pwm_data pwm_data_v2 = {
...@@ -296,9 +255,9 @@ static const struct rockchip_pwm_data pwm_data_v2 = { ...@@ -296,9 +255,9 @@ static const struct rockchip_pwm_data pwm_data_v2 = {
}, },
.prescaler = 1, .prescaler = 1,
.supports_polarity = true, .supports_polarity = true,
.ops = &rockchip_pwm_ops_v2, .supports_lock = false,
.set_enable = rockchip_pwm_set_enable_v2, .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
.get_state = rockchip_pwm_get_state_v2, PWM_CONTINUOUS,
}; };
static const struct rockchip_pwm_data pwm_data_vop = { static const struct rockchip_pwm_data pwm_data_vop = {
...@@ -310,15 +269,30 @@ static const struct rockchip_pwm_data pwm_data_vop = { ...@@ -310,15 +269,30 @@ static const struct rockchip_pwm_data pwm_data_vop = {
}, },
.prescaler = 1, .prescaler = 1,
.supports_polarity = true, .supports_polarity = true,
.ops = &rockchip_pwm_ops_v2, .supports_lock = false,
.set_enable = rockchip_pwm_set_enable_v2, .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
.get_state = rockchip_pwm_get_state_v2, 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[] = { static const struct of_device_id rockchip_pwm_dt_ids[] = {
{ .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1}, { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1},
{ .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2}, { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2},
{ .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop}, { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop},
{ .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3},
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids);
...@@ -328,7 +302,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) ...@@ -328,7 +302,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
const struct of_device_id *id; const struct of_device_id *id;
struct rockchip_pwm_chip *pc; struct rockchip_pwm_chip *pc;
struct resource *r; struct resource *r;
int ret; int ret, count;
id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);
if (!id) if (!id)
...@@ -343,19 +317,49 @@ static int rockchip_pwm_probe(struct platform_device *pdev) ...@@ -343,19 +317,49 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
if (IS_ERR(pc->base)) if (IS_ERR(pc->base))
return PTR_ERR(pc->base); return PTR_ERR(pc->base);
pc->clk = devm_clk_get(&pdev->dev, NULL); pc->clk = devm_clk_get(&pdev->dev, "pwm");
if (IS_ERR(pc->clk)) if (IS_ERR(pc->clk)) {
return PTR_ERR(pc->clk); pc->clk = devm_clk_get(&pdev->dev, NULL);
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); 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; 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); platform_set_drvdata(pdev, pc);
pc->data = id->data; pc->data = id->data;
pc->chip.dev = &pdev->dev; pc->chip.dev = &pdev->dev;
pc->chip.ops = pc->data->ops; pc->chip.ops = &rockchip_pwm_ops;
pc->chip.base = -1; pc->chip.base = -1;
pc->chip.npwm = 1; pc->chip.npwm = 1;
...@@ -368,12 +372,20 @@ static int rockchip_pwm_probe(struct platform_device *pdev) ...@@ -368,12 +372,20 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
if (ret < 0) { if (ret < 0) {
clk_unprepare(pc->clk); clk_unprepare(pc->clk);
dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); 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. */ /* Keep the PWM clk enabled if the PWM appears to be up and running. */
if (!pwm_is_enabled(pc->chip.pwms)) if (!pwm_is_enabled(pc->chip.pwms))
clk_disable(pc->clk); clk_disable(pc->clk);
return 0;
err_pclk:
clk_unprepare(pc->pclk);
err_clk:
clk_disable_unprepare(pc->clk);
return ret; return ret;
} }
...@@ -395,6 +407,7 @@ static int rockchip_pwm_remove(struct platform_device *pdev) ...@@ -395,6 +407,7 @@ static int rockchip_pwm_remove(struct platform_device *pdev)
if (pwm_is_enabled(pc->chip.pwms)) if (pwm_is_enabled(pc->chip.pwms))
clk_disable(pc->clk); clk_disable(pc->clk);
clk_unprepare(pc->pclk);
clk_unprepare(pc->clk); clk_unprepare(pc->clk);
return pwmchip_remove(&pc->chip); return pwmchip_remove(&pc->chip);
......
...@@ -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");
......
...@@ -122,12 +122,12 @@ struct ehrpwm_context { ...@@ -122,12 +122,12 @@ struct ehrpwm_context {
}; };
struct ehrpwm_pwm_chip { struct ehrpwm_pwm_chip {
struct pwm_chip chip; struct pwm_chip chip;
unsigned int clk_rate; unsigned long clk_rate;
void __iomem *mmio_base; void __iomem *mmio_base;
unsigned long period_cycles[NUM_PWM_CHANNEL]; unsigned long period_cycles[NUM_PWM_CHANNEL];
enum pwm_polarity polarity[NUM_PWM_CHANNEL]; enum pwm_polarity polarity[NUM_PWM_CHANNEL];
struct clk *tbclk; struct clk *tbclk;
struct ehrpwm_context ctx; struct ehrpwm_context ctx;
}; };
...@@ -136,25 +136,26 @@ static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) ...@@ -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); 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); 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(value, base + offset);
} }
static void ehrpwm_modify(void __iomem *base, int offset, static void ehrpwm_modify(void __iomem *base, unsigned int offset, u16 mask,
unsigned short mask, unsigned short val) u16 value)
{ {
unsigned short regval; unsigned short val;
regval = readw(base + offset); val = readw(base + offset);
regval &= ~mask; val &= ~mask;
regval |= val & mask; val |= value & mask;
writew(regval, base + offset); writew(val, base + offset);
} }
/** /**
...@@ -163,14 +164,13 @@ static void ehrpwm_modify(void __iomem *base, int offset, ...@@ -163,14 +164,13 @@ static void ehrpwm_modify(void __iomem *base, int offset,
* @prescale_div: prescaler value set * @prescale_div: prescaler value set
* @tb_clk_div: Time Base Control prescaler bits * @tb_clk_div: Time Base Control prescaler bits
*/ */
static int set_prescale_div(unsigned long rqst_prescaler, static int set_prescale_div(unsigned long rqst_prescaler, u16 *prescale_div,
unsigned short *prescale_div, unsigned short *tb_clk_div) u16 *tb_clk_div)
{ {
unsigned int clkdiv, hspclkdiv; unsigned int clkdiv, hspclkdiv;
for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) { for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) {
for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) { for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) {
/* /*
* calculations for prescaler value : * calculations for prescaler value :
* prescale_div = HSPCLKDIVIDER * CLKDIVIDER. * prescale_div = HSPCLKDIVIDER * CLKDIVIDER.
...@@ -191,13 +191,14 @@ static int set_prescale_div(unsigned long rqst_prescaler, ...@@ -191,13 +191,14 @@ static int set_prescale_div(unsigned long rqst_prescaler,
} }
} }
} }
return 1; return 1;
} }
static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan)
{ {
int aqctl_reg; u16 aqctl_val, aqctl_mask;
unsigned short aqctl_val, aqctl_mask; unsigned int aqctl_reg;
/* /*
* Configure PWM output to HIGH/LOW level on counter * Configure PWM output to HIGH/LOW level on counter
...@@ -232,13 +233,13 @@ static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) ...@@ -232,13 +233,13 @@ static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan)
* duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE
*/ */
static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns) int duty_ns, int period_ns)
{ {
struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); 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 long c;
unsigned long period_cycles, duty_cycles;
unsigned short ps_divval, tb_divval;
int i, cmp_reg;
if (period_ns > NSEC_PER_SEC) if (period_ns > NSEC_PER_SEC)
return -ERANGE; return -ERANGE;
...@@ -272,8 +273,9 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -272,8 +273,9 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
if (i == pwm->hwpwm) if (i == pwm->hwpwm)
continue; continue;
dev_err(chip->dev, "Period value conflicts with channel %d\n", dev_err(chip->dev,
i); "period value conflicts with channel %u\n",
i);
return -EINVAL; return -EINVAL;
} }
} }
...@@ -282,7 +284,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -282,7 +284,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
/* Configure clock prescaler to support Low frequency PWM wave */ /* Configure clock prescaler to support Low frequency PWM wave */
if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval, if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval,
&tb_divval)) { &tb_divval)) {
dev_err(chip->dev, "Unsupported values\n"); dev_err(chip->dev, "Unsupported values\n");
return -EINVAL; return -EINVAL;
} }
...@@ -303,7 +305,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -303,7 +305,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
/* Configure ehrpwm counter for up-count mode */ /* Configure ehrpwm counter for up-count mode */
ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK,
TBCTL_CTRMODE_UP); TBCTL_CTRMODE_UP);
if (pwm->hwpwm == 1) if (pwm->hwpwm == 1)
/* Channel 1 configured with compare B register */ /* Channel 1 configured with compare B register */
...@@ -315,23 +317,26 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -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); ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles);
pm_runtime_put_sync(chip->dev); pm_runtime_put_sync(chip->dev);
return 0; return 0;
} }
static int ehrpwm_pwm_set_polarity(struct pwm_chip *chip, 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); struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
/* Configuration of polarity in hardware delayed, do at enable */ /* Configuration of polarity in hardware delayed, do at enable */
pc->polarity[pwm->hwpwm] = polarity; pc->polarity[pwm->hwpwm] = polarity;
return 0; return 0;
} }
static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
unsigned short aqcsfrc_val, aqcsfrc_mask; u16 aqcsfrc_val, aqcsfrc_mask;
int ret; int ret;
/* Leave clock enabled on enabling PWM */ /* Leave clock enabled on enabling PWM */
...@@ -348,7 +353,7 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -348,7 +353,7 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
/* Changes to shadow mode */ /* Changes to shadow mode */
ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
AQSFRC_RLDCSF_ZRO); AQSFRC_RLDCSF_ZRO);
ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
...@@ -358,20 +363,21 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -358,20 +363,21 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
/* Enable TBCLK before enabling PWM device */ /* Enable TBCLK before enabling PWM device */
ret = clk_enable(pc->tbclk); ret = clk_enable(pc->tbclk);
if (ret) { if (ret) {
dev_err(chip->dev, "Failed to enable TBCLK for %s\n", dev_err(chip->dev, "Failed to enable TBCLK for %s: %d\n",
dev_name(pc->chip.dev)); dev_name(pc->chip.dev), ret);
return ret; return ret;
} }
/* Enable time counter for free_run */ /* Enable time counter for free_run */
ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN);
return 0; return 0;
} }
static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
unsigned short aqcsfrc_val, aqcsfrc_mask; u16 aqcsfrc_val, aqcsfrc_mask;
/* Action Qualifier puts PWM output low forcefully */ /* Action Qualifier puts PWM output low forcefully */
if (pwm->hwpwm) { if (pwm->hwpwm) {
...@@ -387,7 +393,7 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -387,7 +393,7 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
* Action Qualifier control on PWM output from next TBCLK * Action Qualifier control on PWM output from next TBCLK
*/ */
ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
AQSFRC_RLDCSF_IMDT); AQSFRC_RLDCSF_IMDT);
ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
...@@ -415,17 +421,17 @@ static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -415,17 +421,17 @@ static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
} }
static const struct pwm_ops ehrpwm_pwm_ops = { static const struct pwm_ops ehrpwm_pwm_ops = {
.free = ehrpwm_pwm_free, .free = ehrpwm_pwm_free,
.config = ehrpwm_pwm_config, .config = ehrpwm_pwm_config,
.set_polarity = ehrpwm_pwm_set_polarity, .set_polarity = ehrpwm_pwm_set_polarity,
.enable = ehrpwm_pwm_enable, .enable = ehrpwm_pwm_enable,
.disable = ehrpwm_pwm_disable, .disable = ehrpwm_pwm_disable,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static const struct of_device_id ehrpwm_of_match[] = { static const struct of_device_id ehrpwm_of_match[] = {
{ .compatible = "ti,am3352-ehrpwm" }, { .compatible = "ti,am3352-ehrpwm" },
{ .compatible = "ti,am33xx-ehrpwm" }, { .compatible = "ti,am33xx-ehrpwm" },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, ehrpwm_of_match); MODULE_DEVICE_TABLE(of, ehrpwm_of_match);
...@@ -433,10 +439,10 @@ MODULE_DEVICE_TABLE(of, ehrpwm_of_match); ...@@ -433,10 +439,10 @@ MODULE_DEVICE_TABLE(of, ehrpwm_of_match);
static int ehrpwm_pwm_probe(struct platform_device *pdev) static int ehrpwm_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 ehrpwm_pwm_chip *pc;
struct resource *r; struct resource *r;
struct clk *clk; struct clk *clk;
struct ehrpwm_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)
...@@ -489,13 +495,18 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) ...@@ -489,13 +495,18 @@ static int ehrpwm_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);
return ret; goto err_clk_unprepare;
} }
platform_set_drvdata(pdev, pc);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
platform_set_drvdata(pdev, pc);
return 0; return 0;
err_clk_unprepare:
clk_unprepare(pc->tbclk);
return ret;
} }
static int ehrpwm_pwm_remove(struct platform_device *pdev) static int ehrpwm_pwm_remove(struct platform_device *pdev)
...@@ -504,8 +515,8 @@ 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); clk_unprepare(pc->tbclk);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
return pwmchip_remove(&pc->chip); return pwmchip_remove(&pc->chip);
} }
...@@ -513,6 +524,7 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) ...@@ -513,6 +524,7 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev)
static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc)
{ {
pm_runtime_get_sync(pc->chip.dev); pm_runtime_get_sync(pc->chip.dev);
pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL); pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL);
pc->ctx.tbprd = ehrpwm_read(pc->mmio_base, TBPRD); pc->ctx.tbprd = ehrpwm_read(pc->mmio_base, TBPRD);
pc->ctx.cmpa = ehrpwm_read(pc->mmio_base, CMPA); pc->ctx.cmpa = ehrpwm_read(pc->mmio_base, CMPA);
...@@ -521,6 +533,7 @@ static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) ...@@ -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.aqctlb = ehrpwm_read(pc->mmio_base, AQCTLB);
pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC); pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC);
pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC); pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC);
pm_runtime_put_sync(pc->chip.dev); pm_runtime_put_sync(pc->chip.dev);
} }
...@@ -539,9 +552,10 @@ static void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc) ...@@ -539,9 +552,10 @@ static void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc)
static int ehrpwm_pwm_suspend(struct device *dev) static int ehrpwm_pwm_suspend(struct device *dev)
{ {
struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev);
int i; unsigned int i;
ehrpwm_pwm_save_context(pc); ehrpwm_pwm_save_context(pc);
for (i = 0; i < pc->chip.npwm; i++) { for (i = 0; i < pc->chip.npwm; i++) {
struct pwm_device *pwm = &pc->chip.pwms[i]; struct pwm_device *pwm = &pc->chip.pwms[i];
...@@ -551,13 +565,14 @@ static int ehrpwm_pwm_suspend(struct device *dev) ...@@ -551,13 +565,14 @@ static int ehrpwm_pwm_suspend(struct device *dev)
/* Disable explicitly if PWM is running */ /* Disable explicitly if PWM is running */
pm_runtime_put_sync(dev); pm_runtime_put_sync(dev);
} }
return 0; return 0;
} }
static int ehrpwm_pwm_resume(struct device *dev) static int ehrpwm_pwm_resume(struct device *dev)
{ {
struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev);
int i; unsigned int i;
for (i = 0; i < pc->chip.npwm; i++) { for (i = 0; i < pc->chip.npwm; i++) {
struct pwm_device *pwm = &pc->chip.pwms[i]; struct pwm_device *pwm = &pc->chip.pwms[i];
...@@ -568,24 +583,25 @@ static int ehrpwm_pwm_resume(struct device *dev) ...@@ -568,24 +583,25 @@ static int ehrpwm_pwm_resume(struct device *dev)
/* Enable explicitly if PWM was running */ /* Enable explicitly if PWM was running */
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
} }
ehrpwm_pwm_restore_context(pc); ehrpwm_pwm_restore_context(pc);
return 0; return 0;
} }
#endif #endif
static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend, static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend,
ehrpwm_pwm_resume); ehrpwm_pwm_resume);
static struct platform_driver ehrpwm_pwm_driver = { static struct platform_driver ehrpwm_pwm_driver = {
.driver = { .driver = {
.name = "ehrpwm", .name = "ehrpwm",
.of_match_table = ehrpwm_of_match, .of_match_table = ehrpwm_of_match,
.pm = &ehrpwm_pwm_pm_ops, .pm = &ehrpwm_pwm_pm_ops,
}, },
.probe = ehrpwm_pwm_probe, .probe = ehrpwm_pwm_probe,
.remove = ehrpwm_pwm_remove, .remove = ehrpwm_pwm_remove,
}; };
module_platform_driver(ehrpwm_pwm_driver); module_platform_driver(ehrpwm_pwm_driver);
MODULE_DESCRIPTION("EHRPWM PWM driver"); MODULE_DESCRIPTION("EHRPWM PWM driver");
......
...@@ -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