diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-08 21:06:29 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-08 21:06:29 -0400 |
| commit | 34b20e6df6970e36b93f445669ba5ef7a05fe01a (patch) | |
| tree | fef410a481679304046ad5b2b06579e34fdd1e73 | |
| parent | 06b49ea43c0cdd22625883e555e45e66ef29e201 (diff) | |
| parent | f6306299080bbb1a77ad39494203f5397a5c2630 (diff) | |
Merge tag 'pwm/for-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
Pull pwm changes from Thierry Reding:
"The set of changes for this merge window contains two new drivers: one
for Rockchip SoCs and another for STMicroelectronics STiH4xx SoCs.
The remainder of the changes are the usual small cleanups such as
removing redundant OOM messages, signalling that a PWM chip's
operations can sleep and removing an unneeded dependency"
* tag 'pwm/for-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
pwm: rockchip: Added to support for RK3288 SoC
pwm: rockchip: document RK3288 SoC compatible
pwm: sti: Remove PWM period table
pwm: sti: Sync between enable/disable calls
pwm: sti: Ensure same period values for all channels
pwm: sti: Fix PWM prescaler handling
pwm: sti: Supply Device Tree binding documentation for ST's PWM IP
pwm: sti: Add new driver for ST's PWM IP
pwm: imx: set can_sleep flag for imx_pwm
pwm: lpss: remove dependency on clk framework
pwm: pwm-tipwmss: remove unnecessary OOM messages
pwm: rockchip: document device tree bindings
pwm: add Rockchip SoC PWM support
| -rw-r--r-- | Documentation/devicetree/bindings/pwm/pwm-rockchip.txt | 20 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/pwm/pwm-st.txt | 41 | ||||
| -rw-r--r-- | drivers/pwm/Kconfig | 17 | ||||
| -rw-r--r-- | drivers/pwm/Makefile | 2 | ||||
| -rw-r--r-- | drivers/pwm/pwm-imx.c | 1 | ||||
| -rw-r--r-- | drivers/pwm/pwm-lpss.c | 32 | ||||
| -rw-r--r-- | drivers/pwm/pwm-rockchip.c | 264 | ||||
| -rw-r--r-- | drivers/pwm/pwm-sti.c | 418 | ||||
| -rw-r--r-- | drivers/pwm/pwm-tipwmss.c | 4 |
9 files changed, 774 insertions, 25 deletions
diff --git a/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt new file mode 100644 index 000000000000..d47d15a6a298 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | Rockchip PWM controller | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: should be "rockchip,<name>-pwm" | ||
| 5 | "rockchip,rk2928-pwm": found on RK29XX,RK3066 and RK3188 SoCs | ||
| 6 | "rockchip,rk3288-pwm": found on RK3288 SoC | ||
| 7 | "rockchip,vop-pwm": found integrated in VOP on RK3288 SoC | ||
| 8 | - reg: physical base address and length of the controller's registers | ||
| 9 | - clocks: phandle and clock specifier of the PWM reference clock | ||
| 10 | - #pwm-cells: should be 2. See pwm.txt in this directory for a | ||
| 11 | description of the cell format. | ||
| 12 | |||
| 13 | Example: | ||
| 14 | |||
| 15 | pwm0: pwm@20030000 { | ||
| 16 | compatible = "rockchip,rk2928-pwm"; | ||
| 17 | reg = <0x20030000 0x10>; | ||
| 18 | clocks = <&cru PCLK_PWM01>; | ||
| 19 | #pwm-cells = <2>; | ||
| 20 | }; | ||
diff --git a/Documentation/devicetree/bindings/pwm/pwm-st.txt b/Documentation/devicetree/bindings/pwm/pwm-st.txt new file mode 100644 index 000000000000..84d2fb807d3c --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-st.txt | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | STMicroelectronics PWM driver bindings | ||
| 2 | -------------------------------------- | ||
| 3 | |||
| 4 | Required parameters: | ||
| 5 | - compatible : "st,pwm" | ||
| 6 | - #pwm-cells : Number of cells used to specify a PWM. First cell | ||
| 7 | specifies the per-chip index of the PWM to use and the | ||
| 8 | second cell is the period in nanoseconds - fixed to 2 | ||
| 9 | for STiH41x. | ||
| 10 | - reg : Physical base address and length of the controller's | ||
| 11 | registers. | ||
| 12 | - pinctrl-names: Set to "default". | ||
| 13 | - pinctrl-0: List of phandles pointing to pin configuration nodes | ||
| 14 | for PWM module. | ||
| 15 | For Pinctrl properties, please refer to [1]. | ||
| 16 | - clock-names: Set to "pwm". | ||
| 17 | - clocks: phandle of the clock used by the PWM module. | ||
| 18 | For Clk properties, please refer to [2]. | ||
| 19 | |||
| 20 | Optional properties: | ||
| 21 | - st,pwm-num-chan: Number of available channels. If not passed, the driver | ||
| 22 | will consider single channel by default. | ||
| 23 | |||
| 24 | [1] Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt | ||
| 25 | [2] Documentation/devicetree/bindings/clock/clock-bindings.txt | ||
| 26 | |||
| 27 | Example: | ||
| 28 | |||
| 29 | pwm1: pwm@fe510000 { | ||
| 30 | compatible = "st,pwm"; | ||
| 31 | reg = <0xfe510000 0x68>; | ||
| 32 | #pwm-cells = <2>; | ||
| 33 | pinctrl-names = "default"; | ||
| 34 | pinctrl-0 = <&pinctrl_pwm1_chan0_default | ||
| 35 | &pinctrl_pwm1_chan1_default | ||
| 36 | &pinctrl_pwm1_chan2_default | ||
| 37 | &pinctrl_pwm1_chan3_default>; | ||
| 38 | clocks = <&clk_sysin>; | ||
| 39 | clock-names = "pwm"; | ||
| 40 | st,pwm-num-chan = <4>; | ||
| 41 | }; | ||
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 331dfca415c7..b800783800a3 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
| @@ -206,6 +206,13 @@ config PWM_RENESAS_TPU | |||
| 206 | To compile this driver as a module, choose M here: the module | 206 | To compile this driver as a module, choose M here: the module |
| 207 | will be called pwm-renesas-tpu. | 207 | will be called pwm-renesas-tpu. |
| 208 | 208 | ||
| 209 | config PWM_ROCKCHIP | ||
| 210 | tristate "Rockchip PWM support" | ||
| 211 | depends on ARCH_ROCKCHIP | ||
| 212 | help | ||
| 213 | Generic PWM framework driver for the PWM controller found on | ||
| 214 | Rockchip SoCs. | ||
| 215 | |||
| 209 | config PWM_SAMSUNG | 216 | config PWM_SAMSUNG |
| 210 | tristate "Samsung PWM support" | 217 | tristate "Samsung PWM support" |
| 211 | depends on PLAT_SAMSUNG | 218 | depends on PLAT_SAMSUNG |
| @@ -226,6 +233,16 @@ config PWM_SPEAR | |||
| 226 | To compile this driver as a module, choose M here: the module | 233 | To compile this driver as a module, choose M here: the module |
| 227 | will be called pwm-spear. | 234 | will be called pwm-spear. |
| 228 | 235 | ||
| 236 | config PWM_STI | ||
| 237 | tristate "STiH4xx PWM support" | ||
| 238 | depends on ARCH_STI | ||
| 239 | depends on OF | ||
| 240 | help | ||
| 241 | Generic PWM framework driver for STiH4xx SoCs. | ||
| 242 | |||
| 243 | To compile this driver as a module, choose M here: the module | ||
| 244 | will be called pwm-sti. | ||
| 245 | |||
| 229 | config PWM_TEGRA | 246 | config PWM_TEGRA |
| 230 | tristate "NVIDIA Tegra PWM support" | 247 | tristate "NVIDIA Tegra PWM support" |
| 231 | depends on ARCH_TEGRA | 248 | depends on ARCH_TEGRA |
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 5c86a19d5d39..f8c577d41091 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
| @@ -18,8 +18,10 @@ obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o | |||
| 18 | obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o | 18 | obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o |
| 19 | obj-$(CONFIG_PWM_PXA) += pwm-pxa.o | 19 | obj-$(CONFIG_PWM_PXA) += pwm-pxa.o |
| 20 | obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o | 20 | obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o |
| 21 | obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o | ||
| 21 | obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o | 22 | obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o |
| 22 | obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o | 23 | obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o |
| 24 | obj-$(CONFIG_PWM_STI) += pwm-sti.o | ||
| 23 | obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o | 25 | obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o |
| 24 | obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o | 26 | obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o |
| 25 | obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o | 27 | obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o |
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index d797c7b84c3f..5449d9150d40 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c | |||
| @@ -262,6 +262,7 @@ static int imx_pwm_probe(struct platform_device *pdev) | |||
| 262 | imx->chip.dev = &pdev->dev; | 262 | imx->chip.dev = &pdev->dev; |
| 263 | imx->chip.base = -1; | 263 | imx->chip.base = -1; |
| 264 | imx->chip.npwm = 1; | 264 | imx->chip.npwm = 1; |
| 265 | imx->chip.can_sleep = true; | ||
| 265 | 266 | ||
| 266 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 267 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 267 | imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); | 268 | imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); |
diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 44ce6c6103ae..4df994f72d96 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c | |||
| @@ -14,7 +14,6 @@ | |||
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | #include <linux/acpi.h> | 16 | #include <linux/acpi.h> |
| 17 | #include <linux/clk.h> | ||
| 18 | #include <linux/device.h> | 17 | #include <linux/device.h> |
| 19 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
| 20 | #include <linux/module.h> | 19 | #include <linux/module.h> |
| @@ -37,7 +36,6 @@ static int pci_drv, plat_drv; /* So we know which drivers registered */ | |||
| 37 | struct pwm_lpss_chip { | 36 | struct pwm_lpss_chip { |
| 38 | struct pwm_chip chip; | 37 | struct pwm_chip chip; |
| 39 | void __iomem *regs; | 38 | void __iomem *regs; |
| 40 | struct clk *clk; | ||
| 41 | unsigned long clk_rate; | 39 | unsigned long clk_rate; |
| 42 | }; | 40 | }; |
| 43 | 41 | ||
| @@ -97,11 +95,6 @@ static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 97 | { | 95 | { |
| 98 | struct pwm_lpss_chip *lpwm = to_lpwm(chip); | 96 | struct pwm_lpss_chip *lpwm = to_lpwm(chip); |
| 99 | u32 ctrl; | 97 | u32 ctrl; |
| 100 | int ret; | ||
| 101 | |||
| 102 | ret = clk_prepare_enable(lpwm->clk); | ||
| 103 | if (ret) | ||
| 104 | return ret; | ||
| 105 | 98 | ||
| 106 | ctrl = readl(lpwm->regs + PWM); | 99 | ctrl = readl(lpwm->regs + PWM); |
| 107 | writel(ctrl | PWM_ENABLE, lpwm->regs + PWM); | 100 | writel(ctrl | PWM_ENABLE, lpwm->regs + PWM); |
| @@ -116,8 +109,6 @@ static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 116 | 109 | ||
| 117 | ctrl = readl(lpwm->regs + PWM); | 110 | ctrl = readl(lpwm->regs + PWM); |
| 118 | writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM); | 111 | writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM); |
| 119 | |||
| 120 | clk_disable_unprepare(lpwm->clk); | ||
| 121 | } | 112 | } |
| 122 | 113 | ||
| 123 | static const struct pwm_ops pwm_lpss_ops = { | 114 | static const struct pwm_ops pwm_lpss_ops = { |
| @@ -142,17 +133,7 @@ static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, | |||
| 142 | if (IS_ERR(lpwm->regs)) | 133 | if (IS_ERR(lpwm->regs)) |
| 143 | return ERR_CAST(lpwm->regs); | 134 | return ERR_CAST(lpwm->regs); |
| 144 | 135 | ||
| 145 | if (info) { | 136 | lpwm->clk_rate = info->clk_rate; |
| 146 | lpwm->clk_rate = info->clk_rate; | ||
| 147 | } else { | ||
| 148 | lpwm->clk = devm_clk_get(dev, NULL); | ||
| 149 | if (IS_ERR(lpwm->clk)) { | ||
| 150 | dev_err(dev, "failed to get PWM clock\n"); | ||
| 151 | return ERR_CAST(lpwm->clk); | ||
| 152 | } | ||
| 153 | lpwm->clk_rate = clk_get_rate(lpwm->clk); | ||
| 154 | } | ||
| 155 | |||
| 156 | lpwm->chip.dev = dev; | 137 | lpwm->chip.dev = dev; |
| 157 | lpwm->chip.ops = &pwm_lpss_ops; | 138 | lpwm->chip.ops = &pwm_lpss_ops; |
| 158 | lpwm->chip.base = -1; | 139 | lpwm->chip.base = -1; |
| @@ -221,12 +202,19 @@ static struct pci_driver pwm_lpss_driver_pci = { | |||
| 221 | 202 | ||
| 222 | static int pwm_lpss_probe_platform(struct platform_device *pdev) | 203 | static int pwm_lpss_probe_platform(struct platform_device *pdev) |
| 223 | { | 204 | { |
| 205 | const struct pwm_lpss_boardinfo *info; | ||
| 206 | const struct acpi_device_id *id; | ||
| 224 | struct pwm_lpss_chip *lpwm; | 207 | struct pwm_lpss_chip *lpwm; |
| 225 | struct resource *r; | 208 | struct resource *r; |
| 226 | 209 | ||
| 210 | id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); | ||
| 211 | if (!id) | ||
| 212 | return -ENODEV; | ||
| 213 | |||
| 227 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 214 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 228 | 215 | ||
| 229 | lpwm = pwm_lpss_probe(&pdev->dev, r, NULL); | 216 | info = (struct pwm_lpss_boardinfo *)id->driver_data; |
| 217 | lpwm = pwm_lpss_probe(&pdev->dev, r, info); | ||
| 230 | if (IS_ERR(lpwm)) | 218 | if (IS_ERR(lpwm)) |
| 231 | return PTR_ERR(lpwm); | 219 | return PTR_ERR(lpwm); |
| 232 | 220 | ||
| @@ -242,7 +230,7 @@ static int pwm_lpss_remove_platform(struct platform_device *pdev) | |||
| 242 | } | 230 | } |
| 243 | 231 | ||
| 244 | static const struct acpi_device_id pwm_lpss_acpi_match[] = { | 232 | static const struct acpi_device_id pwm_lpss_acpi_match[] = { |
| 245 | { "80860F09", 0 }, | 233 | { "80860F09", (unsigned long)&byt_info }, |
| 246 | { }, | 234 | { }, |
| 247 | }; | 235 | }; |
| 248 | MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); | 236 | MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); |
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c new file mode 100644 index 000000000000..bdd8644c01cf --- /dev/null +++ b/drivers/pwm/pwm-rockchip.c | |||
| @@ -0,0 +1,264 @@ | |||
| 1 | /* | ||
| 2 | * PWM driver for Rockchip SoCs | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> | ||
| 5 | * Copyright (C) 2014 ROCKCHIP, Inc. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * version 2 as published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/clk.h> | ||
| 13 | #include <linux/io.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/of.h> | ||
| 16 | #include <linux/of_device.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/pwm.h> | ||
| 19 | #include <linux/time.h> | ||
| 20 | |||
| 21 | #define PWM_CTRL_TIMER_EN (1 << 0) | ||
| 22 | #define PWM_CTRL_OUTPUT_EN (1 << 3) | ||
| 23 | |||
| 24 | #define PWM_ENABLE (1 << 0) | ||
| 25 | #define PWM_CONTINUOUS (1 << 1) | ||
| 26 | #define PWM_DUTY_POSITIVE (1 << 3) | ||
| 27 | #define PWM_INACTIVE_NEGATIVE (0 << 4) | ||
| 28 | #define PWM_OUTPUT_LEFT (0 << 5) | ||
| 29 | #define PWM_LP_DISABLE (0 << 8) | ||
| 30 | |||
| 31 | struct rockchip_pwm_chip { | ||
| 32 | struct pwm_chip chip; | ||
| 33 | struct clk *clk; | ||
| 34 | const struct rockchip_pwm_data *data; | ||
| 35 | void __iomem *base; | ||
| 36 | }; | ||
| 37 | |||
| 38 | struct rockchip_pwm_regs { | ||
| 39 | unsigned long duty; | ||
| 40 | unsigned long period; | ||
| 41 | unsigned long cntr; | ||
| 42 | unsigned long ctrl; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct rockchip_pwm_data { | ||
| 46 | struct rockchip_pwm_regs regs; | ||
| 47 | unsigned int prescaler; | ||
| 48 | |||
| 49 | void (*set_enable)(struct pwm_chip *chip, bool enable); | ||
| 50 | }; | ||
| 51 | |||
| 52 | static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) | ||
| 53 | { | ||
| 54 | return container_of(c, struct rockchip_pwm_chip, chip); | ||
| 55 | } | ||
| 56 | |||
| 57 | static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) | ||
| 58 | { | ||
| 59 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | ||
| 60 | u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; | ||
| 61 | u32 val; | ||
| 62 | |||
| 63 | val = readl_relaxed(pc->base + pc->data->regs.ctrl); | ||
| 64 | |||
| 65 | if (enable) | ||
| 66 | val |= enable_conf; | ||
| 67 | else | ||
| 68 | val &= ~enable_conf; | ||
| 69 | |||
| 70 | writel_relaxed(val, pc->base + pc->data->regs.ctrl); | ||
| 71 | } | ||
| 72 | |||
| 73 | static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) | ||
| 74 | { | ||
| 75 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | ||
| 76 | u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | | ||
| 77 | PWM_CONTINUOUS | PWM_DUTY_POSITIVE | | ||
| 78 | PWM_INACTIVE_NEGATIVE; | ||
| 79 | u32 val; | ||
| 80 | |||
| 81 | val = readl_relaxed(pc->base + pc->data->regs.ctrl); | ||
| 82 | |||
| 83 | if (enable) | ||
| 84 | val |= enable_conf; | ||
| 85 | else | ||
| 86 | val &= ~enable_conf; | ||
| 87 | |||
| 88 | writel_relaxed(val, pc->base + pc->data->regs.ctrl); | ||
| 89 | } | ||
| 90 | |||
| 91 | static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 92 | int duty_ns, int period_ns) | ||
| 93 | { | ||
| 94 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | ||
| 95 | unsigned long period, duty; | ||
| 96 | u64 clk_rate, div; | ||
| 97 | int ret; | ||
| 98 | |||
| 99 | clk_rate = clk_get_rate(pc->clk); | ||
| 100 | |||
| 101 | /* | ||
| 102 | * Since period and duty cycle registers have a width of 32 | ||
| 103 | * bits, every possible input period can be obtained using the | ||
| 104 | * default prescaler value for all practical clock rate values. | ||
| 105 | */ | ||
| 106 | div = clk_rate * period_ns; | ||
| 107 | do_div(div, pc->data->prescaler * NSEC_PER_SEC); | ||
| 108 | period = div; | ||
| 109 | |||
| 110 | div = clk_rate * duty_ns; | ||
| 111 | do_div(div, pc->data->prescaler * NSEC_PER_SEC); | ||
| 112 | duty = div; | ||
| 113 | |||
| 114 | ret = clk_enable(pc->clk); | ||
| 115 | if (ret) | ||
| 116 | return ret; | ||
| 117 | |||
| 118 | writel(period, pc->base + pc->data->regs.period); | ||
| 119 | writel(duty, pc->base + pc->data->regs.duty); | ||
| 120 | writel(0, pc->base + pc->data->regs.cntr); | ||
| 121 | |||
| 122 | clk_disable(pc->clk); | ||
| 123 | |||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 128 | { | ||
| 129 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | ||
| 130 | int ret; | ||
| 131 | |||
| 132 | ret = clk_enable(pc->clk); | ||
| 133 | if (ret) | ||
| 134 | return ret; | ||
| 135 | |||
| 136 | pc->data->set_enable(chip, true); | ||
| 137 | |||
| 138 | return 0; | ||
| 139 | } | ||
| 140 | |||
| 141 | static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 142 | { | ||
| 143 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | ||
| 144 | |||
| 145 | pc->data->set_enable(chip, false); | ||
| 146 | |||
| 147 | clk_disable(pc->clk); | ||
| 148 | } | ||
| 149 | |||
| 150 | static const struct pwm_ops rockchip_pwm_ops = { | ||
| 151 | .config = rockchip_pwm_config, | ||
| 152 | .enable = rockchip_pwm_enable, | ||
| 153 | .disable = rockchip_pwm_disable, | ||
| 154 | .owner = THIS_MODULE, | ||
| 155 | }; | ||
| 156 | |||
| 157 | static const struct rockchip_pwm_data pwm_data_v1 = { | ||
| 158 | .regs = { | ||
| 159 | .duty = 0x04, | ||
| 160 | .period = 0x08, | ||
| 161 | .cntr = 0x00, | ||
| 162 | .ctrl = 0x0c, | ||
| 163 | }, | ||
| 164 | .prescaler = 2, | ||
| 165 | .set_enable = rockchip_pwm_set_enable_v1, | ||
| 166 | }; | ||
| 167 | |||
| 168 | static const struct rockchip_pwm_data pwm_data_v2 = { | ||
| 169 | .regs = { | ||
| 170 | .duty = 0x08, | ||
| 171 | .period = 0x04, | ||
| 172 | .cntr = 0x00, | ||
| 173 | .ctrl = 0x0c, | ||
| 174 | }, | ||
| 175 | .prescaler = 1, | ||
| 176 | .set_enable = rockchip_pwm_set_enable_v2, | ||
| 177 | }; | ||
| 178 | |||
| 179 | static const struct rockchip_pwm_data pwm_data_vop = { | ||
| 180 | .regs = { | ||
| 181 | .duty = 0x08, | ||
| 182 | .period = 0x04, | ||
| 183 | .cntr = 0x0c, | ||
| 184 | .ctrl = 0x00, | ||
| 185 | }, | ||
| 186 | .prescaler = 1, | ||
| 187 | .set_enable = rockchip_pwm_set_enable_v2, | ||
| 188 | }; | ||
| 189 | |||
| 190 | static const struct of_device_id rockchip_pwm_dt_ids[] = { | ||
| 191 | { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1}, | ||
| 192 | { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2}, | ||
| 193 | { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop}, | ||
| 194 | { /* sentinel */ } | ||
| 195 | }; | ||
| 196 | MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); | ||
| 197 | |||
| 198 | static int rockchip_pwm_probe(struct platform_device *pdev) | ||
| 199 | { | ||
| 200 | const struct of_device_id *id; | ||
| 201 | struct rockchip_pwm_chip *pc; | ||
| 202 | struct resource *r; | ||
| 203 | int ret; | ||
| 204 | |||
| 205 | id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); | ||
| 206 | if (!id) | ||
| 207 | return -EINVAL; | ||
| 208 | |||
| 209 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); | ||
| 210 | if (!pc) | ||
| 211 | return -ENOMEM; | ||
| 212 | |||
| 213 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 214 | pc->base = devm_ioremap_resource(&pdev->dev, r); | ||
| 215 | if (IS_ERR(pc->base)) | ||
| 216 | return PTR_ERR(pc->base); | ||
| 217 | |||
| 218 | pc->clk = devm_clk_get(&pdev->dev, NULL); | ||
| 219 | if (IS_ERR(pc->clk)) | ||
| 220 | return PTR_ERR(pc->clk); | ||
| 221 | |||
| 222 | ret = clk_prepare(pc->clk); | ||
| 223 | if (ret) | ||
| 224 | return ret; | ||
| 225 | |||
| 226 | platform_set_drvdata(pdev, pc); | ||
| 227 | |||
| 228 | pc->data = id->data; | ||
| 229 | pc->chip.dev = &pdev->dev; | ||
| 230 | pc->chip.ops = &rockchip_pwm_ops; | ||
| 231 | pc->chip.base = -1; | ||
| 232 | pc->chip.npwm = 1; | ||
| 233 | |||
| 234 | ret = pwmchip_add(&pc->chip); | ||
| 235 | if (ret < 0) { | ||
| 236 | clk_unprepare(pc->clk); | ||
| 237 | dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); | ||
| 238 | } | ||
| 239 | |||
| 240 | return ret; | ||
| 241 | } | ||
| 242 | |||
| 243 | static int rockchip_pwm_remove(struct platform_device *pdev) | ||
| 244 | { | ||
| 245 | struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev); | ||
| 246 | |||
| 247 | clk_unprepare(pc->clk); | ||
| 248 | |||
| 249 | return pwmchip_remove(&pc->chip); | ||
| 250 | } | ||
| 251 | |||
| 252 | static struct platform_driver rockchip_pwm_driver = { | ||
| 253 | .driver = { | ||
| 254 | .name = "rockchip-pwm", | ||
| 255 | .of_match_table = rockchip_pwm_dt_ids, | ||
| 256 | }, | ||
| 257 | .probe = rockchip_pwm_probe, | ||
| 258 | .remove = rockchip_pwm_remove, | ||
| 259 | }; | ||
| 260 | module_platform_driver(rockchip_pwm_driver); | ||
| 261 | |||
| 262 | MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); | ||
| 263 | MODULE_DESCRIPTION("Rockchip SoC PWM driver"); | ||
| 264 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c new file mode 100644 index 000000000000..b95115cdaea7 --- /dev/null +++ b/drivers/pwm/pwm-sti.c | |||
| @@ -0,0 +1,418 @@ | |||
| 1 | /* | ||
| 2 | * PWM device driver for ST SoCs. | ||
| 3 | * Author: Ajit Pal Singh <ajitpal.singh@st.com> | ||
| 4 | * | ||
| 5 | * Copyright (C) 2013-2014 STMicroelectronics (R&D) Limited | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/clk.h> | ||
| 14 | #include <linux/math64.h> | ||
| 15 | #include <linux/mfd/syscon.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/of.h> | ||
| 18 | #include <linux/platform_device.h> | ||
| 19 | #include <linux/pwm.h> | ||
| 20 | #include <linux/regmap.h> | ||
| 21 | #include <linux/slab.h> | ||
| 22 | #include <linux/time.h> | ||
| 23 | |||
| 24 | #define STI_DS_REG(ch) (4 * (ch)) /* Channel's Duty Cycle register */ | ||
| 25 | #define STI_PWMCR 0x50 /* Control/Config register */ | ||
| 26 | #define STI_INTEN 0x54 /* Interrupt Enable/Disable register */ | ||
| 27 | #define PWM_PRESCALE_LOW_MASK 0x0f | ||
| 28 | #define PWM_PRESCALE_HIGH_MASK 0xf0 | ||
| 29 | |||
| 30 | /* Regfield IDs */ | ||
| 31 | enum { | ||
| 32 | PWMCLK_PRESCALE_LOW, | ||
| 33 | PWMCLK_PRESCALE_HIGH, | ||
| 34 | PWM_EN, | ||
| 35 | PWM_INT_EN, | ||
| 36 | |||
| 37 | /* Keep last */ | ||
| 38 | MAX_REGFIELDS | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct sti_pwm_compat_data { | ||
| 42 | const struct reg_field *reg_fields; | ||
| 43 | unsigned int num_chan; | ||
| 44 | unsigned int max_pwm_cnt; | ||
| 45 | unsigned int max_prescale; | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct sti_pwm_chip { | ||
| 49 | struct device *dev; | ||
| 50 | struct clk *clk; | ||
| 51 | unsigned long clk_rate; | ||
| 52 | struct regmap *regmap; | ||
| 53 | struct sti_pwm_compat_data *cdata; | ||
| 54 | struct regmap_field *prescale_low; | ||
| 55 | struct regmap_field *prescale_high; | ||
| 56 | struct regmap_field *pwm_en; | ||
| 57 | struct regmap_field *pwm_int_en; | ||
| 58 | struct pwm_chip chip; | ||
| 59 | struct pwm_device *cur; | ||
| 60 | unsigned int en_count; | ||
| 61 | struct mutex sti_pwm_lock; /* To sync between enable/disable calls */ | ||
| 62 | void __iomem *mmio; | ||
| 63 | }; | ||
| 64 | |||
| 65 | static const struct reg_field sti_pwm_regfields[MAX_REGFIELDS] = { | ||
| 66 | [PWMCLK_PRESCALE_LOW] = REG_FIELD(STI_PWMCR, 0, 3), | ||
| 67 | [PWMCLK_PRESCALE_HIGH] = REG_FIELD(STI_PWMCR, 11, 14), | ||
| 68 | [PWM_EN] = REG_FIELD(STI_PWMCR, 9, 9), | ||
| 69 | [PWM_INT_EN] = REG_FIELD(STI_INTEN, 0, 0), | ||
| 70 | }; | ||
| 71 | |||
| 72 | static inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip) | ||
| 73 | { | ||
| 74 | return container_of(chip, struct sti_pwm_chip, chip); | ||
| 75 | } | ||
| 76 | |||
| 77 | /* | ||
| 78 | * Calculate the prescaler value corresponding to the period. | ||
| 79 | */ | ||
| 80 | static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period, | ||
| 81 | unsigned int *prescale) | ||
| 82 | { | ||
| 83 | struct sti_pwm_compat_data *cdata = pc->cdata; | ||
| 84 | unsigned long val; | ||
| 85 | unsigned int ps; | ||
| 86 | |||
| 87 | /* | ||
| 88 | * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_count + 1)) - 1 | ||
| 89 | */ | ||
| 90 | val = NSEC_PER_SEC / pc->clk_rate; | ||
| 91 | val *= cdata->max_pwm_cnt + 1; | ||
| 92 | |||
| 93 | if (period % val) { | ||
| 94 | return -EINVAL; | ||
| 95 | } else { | ||
| 96 | ps = period / val - 1; | ||
| 97 | if (ps > cdata->max_prescale) | ||
| 98 | return -EINVAL; | ||
| 99 | } | ||
| 100 | *prescale = ps; | ||
| 101 | |||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | |||
| 105 | /* Calculate the number of PWM devices configured with a period. */ | ||
| 106 | static unsigned int sti_pwm_count_configured(struct pwm_chip *chip) | ||
| 107 | { | ||
| 108 | struct pwm_device *pwm; | ||
| 109 | unsigned int ncfg = 0; | ||
| 110 | unsigned int i; | ||
| 111 | |||
| 112 | for (i = 0; i < chip->npwm; i++) { | ||
| 113 | pwm = &chip->pwms[i]; | ||
| 114 | if (test_bit(PWMF_REQUESTED, &pwm->flags)) { | ||
| 115 | if (pwm_get_period(pwm)) | ||
| 116 | ncfg++; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | return ncfg; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* | ||
| 124 | * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. | ||
| 125 | * The only way to change the period (apart from changing the PWM input clock) | ||
| 126 | * is to change the PWM clock prescaler. | ||
| 127 | * The prescaler is of 8 bits, so 256 prescaler values and hence | ||
| 128 | * 256 possible period values are supported (for a particular clock rate). | ||
| 129 | * The requested period will be applied only if it matches one of these | ||
| 130 | * 256 values. | ||
| 131 | */ | ||
| 132 | static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 133 | int duty_ns, int period_ns) | ||
| 134 | { | ||
| 135 | struct sti_pwm_chip *pc = to_sti_pwmchip(chip); | ||
| 136 | struct sti_pwm_compat_data *cdata = pc->cdata; | ||
| 137 | struct pwm_device *cur = pc->cur; | ||
| 138 | struct device *dev = pc->dev; | ||
| 139 | unsigned int prescale = 0, pwmvalx; | ||
| 140 | int ret; | ||
| 141 | unsigned int ncfg; | ||
| 142 | bool period_same = false; | ||
| 143 | |||
| 144 | ncfg = sti_pwm_count_configured(chip); | ||
| 145 | if (ncfg) | ||
| 146 | period_same = (period_ns == pwm_get_period(cur)); | ||
| 147 | |||
| 148 | /* Allow configuration changes if one of the | ||
| 149 | * following conditions satisfy. | ||
| 150 | * 1. No channels have been configured. | ||
| 151 | * 2. Only one channel has been configured and the new request | ||
| 152 | * is for the same channel. | ||
| 153 | * 3. Only one channel has been configured and the new request is | ||
| 154 | * for a new channel and period of the new channel is same as | ||
| 155 | * the current configured period. | ||
| 156 | * 4. More than one channels are configured and period of the new | ||
| 157 | * requestis the same as the current period. | ||
| 158 | */ | ||
| 159 | if (!ncfg || | ||
| 160 | ((ncfg == 1) && (pwm->hwpwm == cur->hwpwm)) || | ||
| 161 | ((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) || | ||
| 162 | ((ncfg > 1) && period_same)) { | ||
| 163 | /* Enable clock before writing to PWM registers. */ | ||
| 164 | ret = clk_enable(pc->clk); | ||
| 165 | if (ret) | ||
| 166 | return ret; | ||
| 167 | |||
| 168 | if (!period_same) { | ||
| 169 | ret = sti_pwm_get_prescale(pc, period_ns, &prescale); | ||
| 170 | if (ret) | ||
| 171 | goto clk_dis; | ||
| 172 | |||
| 173 | ret = | ||
| 174 | regmap_field_write(pc->prescale_low, | ||
| 175 | prescale & PWM_PRESCALE_LOW_MASK); | ||
| 176 | if (ret) | ||
| 177 | goto clk_dis; | ||
| 178 | |||
| 179 | ret = | ||
| 180 | regmap_field_write(pc->prescale_high, | ||
| 181 | (prescale & PWM_PRESCALE_HIGH_MASK) >> 4); | ||
| 182 | if (ret) | ||
| 183 | goto clk_dis; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* | ||
| 187 | * When PWMVal == 0, PWM pulse = 1 local clock cycle. | ||
| 188 | * When PWMVal == max_pwm_count, | ||
| 189 | * PWM pulse = (max_pwm_count + 1) local cycles, | ||
| 190 | * that is continuous pulse: signal never goes low. | ||
| 191 | */ | ||
| 192 | pwmvalx = cdata->max_pwm_cnt * duty_ns / period_ns; | ||
| 193 | |||
| 194 | ret = regmap_write(pc->regmap, STI_DS_REG(pwm->hwpwm), pwmvalx); | ||
| 195 | if (ret) | ||
| 196 | goto clk_dis; | ||
| 197 | |||
| 198 | ret = regmap_field_write(pc->pwm_int_en, 0); | ||
| 199 | |||
| 200 | pc->cur = pwm; | ||
| 201 | |||
| 202 | dev_dbg(dev, "prescale:%u, period:%i, duty:%i, pwmvalx:%u\n", | ||
| 203 | prescale, period_ns, duty_ns, pwmvalx); | ||
| 204 | } else { | ||
| 205 | return -EINVAL; | ||
| 206 | } | ||
| 207 | |||
| 208 | clk_dis: | ||
| 209 | clk_disable(pc->clk); | ||
| 210 | return ret; | ||
| 211 | } | ||
| 212 | |||
| 213 | static int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 214 | { | ||
| 215 | struct sti_pwm_chip *pc = to_sti_pwmchip(chip); | ||
| 216 | struct device *dev = pc->dev; | ||
| 217 | int ret = 0; | ||
| 218 | |||
| 219 | /* | ||
| 220 | * Since we have a common enable for all PWM channels, | ||
| 221 | * do not enable if already enabled. | ||
| 222 | */ | ||
| 223 | mutex_lock(&pc->sti_pwm_lock); | ||
| 224 | if (!pc->en_count) { | ||
| 225 | ret = clk_enable(pc->clk); | ||
| 226 | if (ret) | ||
| 227 | goto out; | ||
| 228 | |||
| 229 | ret = regmap_field_write(pc->pwm_en, 1); | ||
| 230 | if (ret) { | ||
| 231 | dev_err(dev, "failed to enable PWM device:%d\n", | ||
| 232 | pwm->hwpwm); | ||
| 233 | goto out; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | pc->en_count++; | ||
| 237 | out: | ||
| 238 | mutex_unlock(&pc->sti_pwm_lock); | ||
| 239 | return ret; | ||
| 240 | } | ||
| 241 | |||
| 242 | static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 243 | { | ||
| 244 | struct sti_pwm_chip *pc = to_sti_pwmchip(chip); | ||
| 245 | |||
| 246 | mutex_lock(&pc->sti_pwm_lock); | ||
| 247 | if (--pc->en_count) { | ||
| 248 | mutex_unlock(&pc->sti_pwm_lock); | ||
| 249 | return; | ||
| 250 | } | ||
| 251 | regmap_field_write(pc->pwm_en, 0); | ||
| 252 | |||
| 253 | clk_disable(pc->clk); | ||
| 254 | mutex_unlock(&pc->sti_pwm_lock); | ||
| 255 | } | ||
| 256 | |||
| 257 | static const struct pwm_ops sti_pwm_ops = { | ||
| 258 | .config = sti_pwm_config, | ||
| 259 | .enable = sti_pwm_enable, | ||
| 260 | .disable = sti_pwm_disable, | ||
| 261 | .owner = THIS_MODULE, | ||
| 262 | }; | ||
| 263 | |||
| 264 | static int sti_pwm_probe_dt(struct sti_pwm_chip *pc) | ||
| 265 | { | ||
| 266 | struct device *dev = pc->dev; | ||
| 267 | const struct reg_field *reg_fields; | ||
| 268 | struct device_node *np = dev->of_node; | ||
| 269 | struct sti_pwm_compat_data *cdata = pc->cdata; | ||
| 270 | u32 num_chan; | ||
| 271 | |||
| 272 | of_property_read_u32(np, "st,pwm-num-chan", &num_chan); | ||
| 273 | if (num_chan) | ||
| 274 | cdata->num_chan = num_chan; | ||
| 275 | |||
| 276 | reg_fields = cdata->reg_fields; | ||
| 277 | |||
| 278 | pc->prescale_low = devm_regmap_field_alloc(dev, pc->regmap, | ||
| 279 | reg_fields[PWMCLK_PRESCALE_LOW]); | ||
| 280 | if (IS_ERR(pc->prescale_low)) | ||
| 281 | return PTR_ERR(pc->prescale_low); | ||
| 282 | |||
| 283 | pc->prescale_high = devm_regmap_field_alloc(dev, pc->regmap, | ||
| 284 | reg_fields[PWMCLK_PRESCALE_HIGH]); | ||
| 285 | if (IS_ERR(pc->prescale_high)) | ||
| 286 | return PTR_ERR(pc->prescale_high); | ||
| 287 | |||
| 288 | pc->pwm_en = devm_regmap_field_alloc(dev, pc->regmap, | ||
| 289 | reg_fields[PWM_EN]); | ||
| 290 | if (IS_ERR(pc->pwm_en)) | ||
| 291 | return PTR_ERR(pc->pwm_en); | ||
| 292 | |||
| 293 | pc->pwm_int_en = devm_regmap_field_alloc(dev, pc->regmap, | ||
| 294 | reg_fields[PWM_INT_EN]); | ||
| 295 | if (IS_ERR(pc->pwm_int_en)) | ||
| 296 | return PTR_ERR(pc->pwm_int_en); | ||
| 297 | |||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | static const struct regmap_config sti_pwm_regmap_config = { | ||
| 302 | .reg_bits = 32, | ||
| 303 | .val_bits = 32, | ||
| 304 | .reg_stride = 4, | ||
| 305 | }; | ||
| 306 | |||
| 307 | static int sti_pwm_probe(struct platform_device *pdev) | ||
| 308 | { | ||
| 309 | struct device *dev = &pdev->dev; | ||
| 310 | struct sti_pwm_compat_data *cdata; | ||
| 311 | struct sti_pwm_chip *pc; | ||
| 312 | struct resource *res; | ||
| 313 | int ret; | ||
| 314 | |||
| 315 | pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); | ||
| 316 | if (!pc) | ||
| 317 | return -ENOMEM; | ||
| 318 | |||
| 319 | cdata = devm_kzalloc(dev, sizeof(*cdata), GFP_KERNEL); | ||
| 320 | if (!cdata) | ||
| 321 | return -ENOMEM; | ||
| 322 | |||
| 323 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 324 | |||
| 325 | pc->mmio = devm_ioremap_resource(dev, res); | ||
| 326 | if (IS_ERR(pc->mmio)) | ||
| 327 | return PTR_ERR(pc->mmio); | ||
| 328 | |||
| 329 | pc->regmap = devm_regmap_init_mmio(dev, pc->mmio, | ||
| 330 | &sti_pwm_regmap_config); | ||
| 331 | if (IS_ERR(pc->regmap)) | ||
| 332 | return PTR_ERR(pc->regmap); | ||
| 333 | |||
| 334 | /* | ||
| 335 | * Setup PWM data with default values: some values could be replaced | ||
| 336 | * with specific ones provided from Device Tree. | ||
| 337 | */ | ||
| 338 | cdata->reg_fields = &sti_pwm_regfields[0]; | ||
| 339 | cdata->max_prescale = 0xff; | ||
| 340 | cdata->max_pwm_cnt = 255; | ||
| 341 | cdata->num_chan = 1; | ||
| 342 | |||
| 343 | pc->cdata = cdata; | ||
| 344 | pc->dev = dev; | ||
| 345 | pc->en_count = 0; | ||
| 346 | mutex_init(&pc->sti_pwm_lock); | ||
| 347 | |||
| 348 | ret = sti_pwm_probe_dt(pc); | ||
| 349 | if (ret) | ||
| 350 | return ret; | ||
| 351 | |||
| 352 | pc->clk = of_clk_get_by_name(dev->of_node, "pwm"); | ||
| 353 | if (IS_ERR(pc->clk)) { | ||
| 354 | dev_err(dev, "failed to get PWM clock\n"); | ||
| 355 | return PTR_ERR(pc->clk); | ||
| 356 | } | ||
| 357 | |||
| 358 | pc->clk_rate = clk_get_rate(pc->clk); | ||
| 359 | if (!pc->clk_rate) { | ||
| 360 | dev_err(dev, "failed to get clock rate\n"); | ||
| 361 | return -EINVAL; | ||
| 362 | } | ||
| 363 | |||
| 364 | ret = clk_prepare(pc->clk); | ||
| 365 | if (ret) { | ||
| 366 | dev_err(dev, "failed to prepare clock\n"); | ||
| 367 | return ret; | ||
| 368 | } | ||
| 369 | |||
| 370 | pc->chip.dev = dev; | ||
| 371 | pc->chip.ops = &sti_pwm_ops; | ||
| 372 | pc->chip.base = -1; | ||
| 373 | pc->chip.npwm = pc->cdata->num_chan; | ||
| 374 | pc->chip.can_sleep = true; | ||
| 375 | |||
| 376 | ret = pwmchip_add(&pc->chip); | ||
| 377 | if (ret < 0) { | ||
| 378 | clk_unprepare(pc->clk); | ||
| 379 | return ret; | ||
| 380 | } | ||
| 381 | |||
| 382 | platform_set_drvdata(pdev, pc); | ||
| 383 | |||
| 384 | return 0; | ||
| 385 | } | ||
| 386 | |||
| 387 | static int sti_pwm_remove(struct platform_device *pdev) | ||
| 388 | { | ||
| 389 | struct sti_pwm_chip *pc = platform_get_drvdata(pdev); | ||
| 390 | unsigned int i; | ||
| 391 | |||
| 392 | for (i = 0; i < pc->cdata->num_chan; i++) | ||
| 393 | pwm_disable(&pc->chip.pwms[i]); | ||
| 394 | |||
| 395 | clk_unprepare(pc->clk); | ||
| 396 | |||
| 397 | return pwmchip_remove(&pc->chip); | ||
| 398 | } | ||
| 399 | |||
| 400 | static const struct of_device_id sti_pwm_of_match[] = { | ||
| 401 | { .compatible = "st,sti-pwm", }, | ||
| 402 | { /* sentinel */ } | ||
| 403 | }; | ||
| 404 | MODULE_DEVICE_TABLE(of, sti_pwm_of_match); | ||
| 405 | |||
| 406 | static struct platform_driver sti_pwm_driver = { | ||
| 407 | .driver = { | ||
| 408 | .name = "sti-pwm", | ||
| 409 | .of_match_table = sti_pwm_of_match, | ||
| 410 | }, | ||
| 411 | .probe = sti_pwm_probe, | ||
| 412 | .remove = sti_pwm_remove, | ||
| 413 | }; | ||
| 414 | module_platform_driver(sti_pwm_driver); | ||
| 415 | |||
| 416 | MODULE_AUTHOR("Ajit Pal Singh <ajitpal.singh@st.com>"); | ||
| 417 | MODULE_DESCRIPTION("STMicroelectronics ST PWM driver"); | ||
| 418 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/pwm/pwm-tipwmss.c index 3b119bc2c3c6..67481dc6da3f 100644 --- a/drivers/pwm/pwm-tipwmss.c +++ b/drivers/pwm/pwm-tipwmss.c | |||
| @@ -62,10 +62,8 @@ static int pwmss_probe(struct platform_device *pdev) | |||
| 62 | struct device_node *node = pdev->dev.of_node; | 62 | struct device_node *node = pdev->dev.of_node; |
| 63 | 63 | ||
| 64 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | 64 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
| 65 | if (!info) { | 65 | if (!info) |
| 66 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
| 67 | return -ENOMEM; | 66 | return -ENOMEM; |
| 68 | } | ||
| 69 | 67 | ||
| 70 | mutex_init(&info->pwmss_lock); | 68 | mutex_init(&info->pwmss_lock); |
| 71 | 69 | ||
