diff options
| -rw-r--r-- | Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt | 19 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/pwm/pwm-rockchip.txt | 4 | ||||
| -rw-r--r-- | drivers/pwm/Kconfig | 22 | ||||
| -rw-r--r-- | drivers/pwm/Makefile | 2 | ||||
| -rw-r--r-- | drivers/pwm/core.c | 31 | ||||
| -rw-r--r-- | drivers/pwm/pwm-atmel.c | 24 | ||||
| -rw-r--r-- | drivers/pwm/pwm-fsl-ftm.c | 90 | ||||
| -rw-r--r-- | drivers/pwm/pwm-imx.c | 71 | ||||
| -rw-r--r-- | drivers/pwm/pwm-lpss-pci.c | 64 | ||||
| -rw-r--r-- | drivers/pwm/pwm-lpss-platform.c | 68 | ||||
| -rw-r--r-- | drivers/pwm/pwm-lpss.c | 137 | ||||
| -rw-r--r-- | drivers/pwm/pwm-lpss.h | 32 | ||||
| -rw-r--r-- | drivers/pwm/pwm-rockchip.c | 57 |
13 files changed, 399 insertions, 222 deletions
diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt index 0bda229a6171..3899d6a557c1 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt | |||
| @@ -1,5 +1,20 @@ | |||
| 1 | Freescale FlexTimer Module (FTM) PWM controller | 1 | Freescale FlexTimer Module (FTM) PWM controller |
| 2 | 2 | ||
| 3 | The same FTM PWM device can have a different endianness on different SoCs. The | ||
| 4 | device tree provides a property to describing this so that an operating system | ||
| 5 | device driver can handle all variants of the device. Refer to the table below | ||
| 6 | for the endianness of the FTM PWM block as integrated into the existing SoCs: | ||
| 7 | |||
| 8 | SoC | FTM-PWM endianness | ||
| 9 | --------+------------------- | ||
| 10 | Vybrid | LE | ||
| 11 | LS1 | BE | ||
| 12 | LS2 | LE | ||
| 13 | |||
| 14 | Please see ../regmap/regmap.txt for more detail about how to specify endian | ||
| 15 | modes in device tree. | ||
| 16 | |||
| 17 | |||
| 3 | Required properties: | 18 | Required properties: |
| 4 | - compatible: Should be "fsl,vf610-ftm-pwm". | 19 | - compatible: Should be "fsl,vf610-ftm-pwm". |
| 5 | - reg: Physical base address and length of the controller's registers | 20 | - reg: Physical base address and length of the controller's registers |
| @@ -16,7 +31,8 @@ Required properties: | |||
| 16 | - pinctrl-names: Must contain a "default" entry. | 31 | - pinctrl-names: Must contain a "default" entry. |
| 17 | - pinctrl-NNN: One property must exist for each entry in pinctrl-names. | 32 | - pinctrl-NNN: One property must exist for each entry in pinctrl-names. |
| 18 | See pinctrl/pinctrl-bindings.txt for details of the property values. | 33 | See pinctrl/pinctrl-bindings.txt for details of the property values. |
| 19 | 34 | - big-endian: Boolean property, required if the FTM PWM registers use a big- | |
| 35 | endian rather than little-endian layout. | ||
| 20 | 36 | ||
| 21 | Example: | 37 | Example: |
| 22 | 38 | ||
| @@ -32,4 +48,5 @@ pwm0: pwm@40038000 { | |||
| 32 | <&clks VF610_CLK_FTM0_EXT_FIX_EN>; | 48 | <&clks VF610_CLK_FTM0_EXT_FIX_EN>; |
| 33 | pinctrl-names = "default"; | 49 | pinctrl-names = "default"; |
| 34 | pinctrl-0 = <&pinctrl_pwm0_1>; | 50 | pinctrl-0 = <&pinctrl_pwm0_1>; |
| 51 | big-endian; | ||
| 35 | }; | 52 | }; |
diff --git a/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt index d47d15a6a298..b8be3d09ee26 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt | |||
| @@ -7,8 +7,8 @@ Required properties: | |||
| 7 | "rockchip,vop-pwm": found integrated in VOP 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 | 8 | - reg: physical base address and length of the controller's registers |
| 9 | - clocks: phandle and clock specifier of the PWM reference clock | 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 | 10 | - #pwm-cells: must be 2 (rk2928) or 3 (rk3288). See pwm.txt in this directory |
| 11 | description of the cell format. | 11 | for a description of the cell format. |
| 12 | 12 | ||
| 13 | Example: | 13 | Example: |
| 14 | 14 | ||
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index b800783800a3..ef2dd2e4754b 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
| @@ -83,6 +83,7 @@ config PWM_BFIN | |||
| 83 | config PWM_CLPS711X | 83 | config PWM_CLPS711X |
| 84 | tristate "CLPS711X PWM support" | 84 | tristate "CLPS711X PWM support" |
| 85 | depends on ARCH_CLPS711X || COMPILE_TEST | 85 | depends on ARCH_CLPS711X || COMPILE_TEST |
| 86 | depends on HAS_IOMEM | ||
| 86 | help | 87 | help |
| 87 | Generic PWM framework driver for Cirrus Logic CLPS711X. | 88 | Generic PWM framework driver for Cirrus Logic CLPS711X. |
| 88 | 89 | ||
| @@ -101,6 +102,7 @@ config PWM_EP93XX | |||
| 101 | config PWM_FSL_FTM | 102 | config PWM_FSL_FTM |
| 102 | tristate "Freescale FlexTimer Module (FTM) PWM support" | 103 | tristate "Freescale FlexTimer Module (FTM) PWM support" |
| 103 | depends on OF | 104 | depends on OF |
| 105 | select REGMAP_MMIO | ||
| 104 | help | 106 | help |
| 105 | Generic FTM PWM framework driver for Freescale VF610 and | 107 | Generic FTM PWM framework driver for Freescale VF610 and |
| 106 | Layerscape LS-1 SoCs. | 108 | Layerscape LS-1 SoCs. |
| @@ -149,7 +151,7 @@ config PWM_LPC32XX | |||
| 149 | 151 | ||
| 150 | config PWM_LPSS | 152 | config PWM_LPSS |
| 151 | tristate "Intel LPSS PWM support" | 153 | tristate "Intel LPSS PWM support" |
| 152 | depends on ACPI | 154 | depends on X86 |
| 153 | help | 155 | help |
| 154 | Generic PWM framework driver for Intel Low Power Subsystem PWM | 156 | Generic PWM framework driver for Intel Low Power Subsystem PWM |
| 155 | controller. | 157 | controller. |
| @@ -157,6 +159,24 @@ config PWM_LPSS | |||
| 157 | To compile this driver as a module, choose M here: the module | 159 | To compile this driver as a module, choose M here: the module |
| 158 | will be called pwm-lpss. | 160 | will be called pwm-lpss. |
| 159 | 161 | ||
| 162 | config PWM_LPSS_PCI | ||
| 163 | tristate "Intel LPSS PWM PCI driver" | ||
| 164 | depends on PWM_LPSS && PCI | ||
| 165 | help | ||
| 166 | The PCI driver for Intel Low Power Subsystem PWM controller. | ||
| 167 | |||
| 168 | To compile this driver as a module, choose M here: the module | ||
| 169 | will be called pwm-lpss-pci. | ||
| 170 | |||
| 171 | config PWM_LPSS_PLATFORM | ||
| 172 | tristate "Intel LPSS PWM platform driver" | ||
| 173 | depends on PWM_LPSS && ACPI | ||
| 174 | help | ||
| 175 | The platform driver for Intel Low Power Subsystem PWM controller. | ||
| 176 | |||
| 177 | To compile this driver as a module, choose M here: the module | ||
| 178 | will be called pwm-lpss-platform. | ||
| 179 | |||
| 160 | config PWM_MXS | 180 | config PWM_MXS |
| 161 | tristate "Freescale MXS PWM support" | 181 | tristate "Freescale MXS PWM support" |
| 162 | depends on ARCH_MXS && OF | 182 | depends on ARCH_MXS && OF |
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index f8c577d41091..c458606c3755 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
| @@ -13,6 +13,8 @@ obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o | |||
| 13 | obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o | 13 | obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o |
| 14 | obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o | 14 | obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o |
| 15 | obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o | 15 | obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o |
| 16 | obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o | ||
| 17 | obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o | ||
| 16 | obj-$(CONFIG_PWM_MXS) += pwm-mxs.o | 18 | obj-$(CONFIG_PWM_MXS) += pwm-mxs.o |
| 17 | obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o | 19 | obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o |
| 18 | obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o | 20 | obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o |
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index d2c35920ff08..966497d10c6e 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c | |||
| @@ -236,7 +236,7 @@ int pwmchip_add(struct pwm_chip *chip) | |||
| 236 | int ret; | 236 | int ret; |
| 237 | 237 | ||
| 238 | if (!chip || !chip->dev || !chip->ops || !chip->ops->config || | 238 | if (!chip || !chip->dev || !chip->ops || !chip->ops->config || |
| 239 | !chip->ops->enable || !chip->ops->disable) | 239 | !chip->ops->enable || !chip->ops->disable || !chip->npwm) |
| 240 | return -EINVAL; | 240 | return -EINVAL; |
| 241 | 241 | ||
| 242 | mutex_lock(&pwm_lock); | 242 | mutex_lock(&pwm_lock); |
| @@ -602,12 +602,9 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) | |||
| 602 | struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER); | 602 | struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER); |
| 603 | const char *dev_id = dev ? dev_name(dev) : NULL; | 603 | const char *dev_id = dev ? dev_name(dev) : NULL; |
| 604 | struct pwm_chip *chip = NULL; | 604 | struct pwm_chip *chip = NULL; |
| 605 | unsigned int index = 0; | ||
| 606 | unsigned int best = 0; | 605 | unsigned int best = 0; |
| 607 | struct pwm_lookup *p; | 606 | struct pwm_lookup *p, *chosen = NULL; |
| 608 | unsigned int match; | 607 | unsigned int match; |
| 609 | unsigned int period; | ||
| 610 | enum pwm_polarity polarity; | ||
| 611 | 608 | ||
| 612 | /* look up via DT first */ | 609 | /* look up via DT first */ |
| 613 | if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) | 610 | if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) |
| @@ -653,10 +650,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) | |||
| 653 | } | 650 | } |
| 654 | 651 | ||
| 655 | if (match > best) { | 652 | if (match > best) { |
| 656 | chip = pwmchip_find_by_name(p->provider); | 653 | chosen = p; |
| 657 | index = p->index; | ||
| 658 | period = p->period; | ||
| 659 | polarity = p->polarity; | ||
| 660 | 654 | ||
| 661 | if (match != 3) | 655 | if (match != 3) |
| 662 | best = match; | 656 | best = match; |
| @@ -665,17 +659,22 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) | |||
| 665 | } | 659 | } |
| 666 | } | 660 | } |
| 667 | 661 | ||
| 668 | mutex_unlock(&pwm_lookup_lock); | 662 | if (!chosen) |
| 663 | goto out; | ||
| 669 | 664 | ||
| 670 | if (chip) | 665 | chip = pwmchip_find_by_name(chosen->provider); |
| 671 | pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id); | 666 | if (!chip) |
| 672 | if (IS_ERR(pwm)) | 667 | goto out; |
| 673 | return pwm; | ||
| 674 | 668 | ||
| 675 | pwm_set_period(pwm, period); | 669 | pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id); |
| 676 | pwm_set_polarity(pwm, polarity); | 670 | if (IS_ERR(pwm)) |
| 671 | goto out; | ||
| 677 | 672 | ||
| 673 | pwm_set_period(pwm, chosen->period); | ||
| 674 | pwm_set_polarity(pwm, chosen->polarity); | ||
| 678 | 675 | ||
| 676 | out: | ||
| 677 | mutex_unlock(&pwm_lookup_lock); | ||
| 679 | return pwm; | 678 | return pwm; |
| 680 | } | 679 | } |
| 681 | EXPORT_SYMBOL_GPL(pwm_get); | 680 | EXPORT_SYMBOL_GPL(pwm_get); |
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 6e700a541ca3..d3c22de9ee47 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c | |||
| @@ -102,7 +102,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 102 | int duty_ns, int period_ns) | 102 | int duty_ns, int period_ns) |
| 103 | { | 103 | { |
| 104 | struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); | 104 | struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); |
| 105 | unsigned long clk_rate, prd, dty; | 105 | unsigned long prd, dty; |
| 106 | unsigned long long div; | 106 | unsigned long long div; |
| 107 | unsigned int pres = 0; | 107 | unsigned int pres = 0; |
| 108 | u32 val; | 108 | u32 val; |
| @@ -113,20 +113,18 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 113 | return -EBUSY; | 113 | return -EBUSY; |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | clk_rate = clk_get_rate(atmel_pwm->clk); | 116 | /* Calculate the period cycles and prescale value */ |
| 117 | div = clk_rate; | 117 | div = (unsigned long long)clk_get_rate(atmel_pwm->clk) * period_ns; |
| 118 | do_div(div, NSEC_PER_SEC); | ||
| 118 | 119 | ||
| 119 | /* Calculate the period cycles */ | ||
| 120 | while (div > PWM_MAX_PRD) { | 120 | while (div > PWM_MAX_PRD) { |
| 121 | div = clk_rate / (1 << pres); | 121 | div >>= 1; |
| 122 | div = div * period_ns; | 122 | pres++; |
| 123 | /* 1/Hz = 100000000 ns */ | 123 | } |
| 124 | do_div(div, 1000000000); | 124 | |
| 125 | 125 | if (pres > PRD_MAX_PRES) { | |
| 126 | if (pres++ > PRD_MAX_PRES) { | 126 | dev_err(chip->dev, "pres exceeds the maximum value\n"); |
| 127 | dev_err(chip->dev, "pres exceeds the maximum value\n"); | 127 | return -EINVAL; |
| 128 | return -EINVAL; | ||
| 129 | } | ||
| 130 | } | 128 | } |
| 131 | 129 | ||
| 132 | /* Calculate the duty cycles */ | 130 | /* Calculate the duty cycles */ |
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index a18bc8fea385..0f2cc7ef7784 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c | |||
| @@ -18,14 +18,14 @@ | |||
| 18 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
| 19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
| 20 | #include <linux/pwm.h> | 20 | #include <linux/pwm.h> |
| 21 | #include <linux/regmap.h> | ||
| 21 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
| 22 | 23 | ||
| 23 | #define FTM_SC 0x00 | 24 | #define FTM_SC 0x00 |
| 24 | #define FTM_SC_CLK_MASK 0x3 | 25 | #define FTM_SC_CLK_MASK_SHIFT 3 |
| 25 | #define FTM_SC_CLK_SHIFT 3 | 26 | #define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT) |
| 26 | #define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_SHIFT) | 27 | #define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT) |
| 27 | #define FTM_SC_PS_MASK 0x7 | 28 | #define FTM_SC_PS_MASK 0x7 |
| 28 | #define FTM_SC_PS_SHIFT 0 | ||
| 29 | 29 | ||
| 30 | #define FTM_CNT 0x04 | 30 | #define FTM_CNT 0x04 |
| 31 | #define FTM_MOD 0x08 | 31 | #define FTM_MOD 0x08 |
| @@ -83,7 +83,7 @@ struct fsl_pwm_chip { | |||
| 83 | unsigned int cnt_select; | 83 | unsigned int cnt_select; |
| 84 | unsigned int clk_ps; | 84 | unsigned int clk_ps; |
| 85 | 85 | ||
| 86 | void __iomem *base; | 86 | struct regmap *regmap; |
| 87 | 87 | ||
| 88 | int period_ns; | 88 | int period_ns; |
| 89 | 89 | ||
| @@ -219,10 +219,11 @@ static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, | |||
| 219 | unsigned long period_ns, | 219 | unsigned long period_ns, |
| 220 | unsigned long duty_ns) | 220 | unsigned long duty_ns) |
| 221 | { | 221 | { |
| 222 | unsigned long long val, duty; | 222 | unsigned long long duty; |
| 223 | u32 val; | ||
| 223 | 224 | ||
| 224 | val = readl(fpc->base + FTM_MOD); | 225 | regmap_read(fpc->regmap, FTM_MOD, &val); |
| 225 | duty = duty_ns * (val + 1); | 226 | duty = (unsigned long long)duty_ns * (val + 1); |
| 226 | do_div(duty, period_ns); | 227 | do_div(duty, period_ns); |
| 227 | 228 | ||
| 228 | return (unsigned long)duty; | 229 | return (unsigned long)duty; |
| @@ -232,7 +233,7 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 232 | int duty_ns, int period_ns) | 233 | int duty_ns, int period_ns) |
| 233 | { | 234 | { |
| 234 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | 235 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
| 235 | u32 val, period, duty; | 236 | u32 period, duty; |
| 236 | 237 | ||
| 237 | mutex_lock(&fpc->lock); | 238 | mutex_lock(&fpc->lock); |
| 238 | 239 | ||
| @@ -257,11 +258,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 257 | return -EINVAL; | 258 | return -EINVAL; |
| 258 | } | 259 | } |
| 259 | 260 | ||
| 260 | val = readl(fpc->base + FTM_SC); | 261 | regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, |
| 261 | val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT); | 262 | fpc->clk_ps); |
| 262 | val |= fpc->clk_ps; | 263 | regmap_write(fpc->regmap, FTM_MOD, period - 1); |
| 263 | writel(val, fpc->base + FTM_SC); | ||
| 264 | writel(period - 1, fpc->base + FTM_MOD); | ||
| 265 | 264 | ||
| 266 | fpc->period_ns = period_ns; | 265 | fpc->period_ns = period_ns; |
| 267 | } | 266 | } |
| @@ -270,8 +269,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 270 | 269 | ||
| 271 | duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); | 270 | duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); |
| 272 | 271 | ||
| 273 | writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm)); | 272 | regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm), |
| 274 | writel(duty, fpc->base + FTM_CV(pwm->hwpwm)); | 273 | FTM_CSC_MSB | FTM_CSC_ELSB); |
| 274 | regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty); | ||
| 275 | 275 | ||
| 276 | return 0; | 276 | return 0; |
| 277 | } | 277 | } |
| @@ -283,31 +283,28 @@ static int fsl_pwm_set_polarity(struct pwm_chip *chip, | |||
| 283 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | 283 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
| 284 | u32 val; | 284 | u32 val; |
| 285 | 285 | ||
| 286 | val = readl(fpc->base + FTM_POL); | 286 | regmap_read(fpc->regmap, FTM_POL, &val); |
| 287 | 287 | ||
| 288 | if (polarity == PWM_POLARITY_INVERSED) | 288 | if (polarity == PWM_POLARITY_INVERSED) |
| 289 | val |= BIT(pwm->hwpwm); | 289 | val |= BIT(pwm->hwpwm); |
| 290 | else | 290 | else |
| 291 | val &= ~BIT(pwm->hwpwm); | 291 | val &= ~BIT(pwm->hwpwm); |
| 292 | 292 | ||
| 293 | writel(val, fpc->base + FTM_POL); | 293 | regmap_write(fpc->regmap, FTM_POL, val); |
| 294 | 294 | ||
| 295 | return 0; | 295 | return 0; |
| 296 | } | 296 | } |
| 297 | 297 | ||
| 298 | static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) | 298 | static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) |
| 299 | { | 299 | { |
| 300 | u32 val; | ||
| 301 | int ret; | 300 | int ret; |
| 302 | 301 | ||
| 303 | if (fpc->use_count != 0) | 302 | if (fpc->use_count != 0) |
| 304 | return 0; | 303 | return 0; |
| 305 | 304 | ||
| 306 | /* select counter clock source */ | 305 | /* select counter clock source */ |
| 307 | val = readl(fpc->base + FTM_SC); | 306 | regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, |
| 308 | val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); | 307 | FTM_SC_CLK(fpc->cnt_select)); |
| 309 | val |= FTM_SC_CLK(fpc->cnt_select); | ||
| 310 | writel(val, fpc->base + FTM_SC); | ||
| 311 | 308 | ||
| 312 | ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); | 309 | ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); |
| 313 | if (ret) | 310 | if (ret) |
| @@ -327,13 +324,10 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) | |||
| 327 | static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | 324 | static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
| 328 | { | 325 | { |
| 329 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | 326 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
| 330 | u32 val; | ||
| 331 | int ret; | 327 | int ret; |
| 332 | 328 | ||
| 333 | mutex_lock(&fpc->lock); | 329 | mutex_lock(&fpc->lock); |
| 334 | val = readl(fpc->base + FTM_OUTMASK); | 330 | regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0); |
| 335 | val &= ~BIT(pwm->hwpwm); | ||
| 336 | writel(val, fpc->base + FTM_OUTMASK); | ||
| 337 | 331 | ||
| 338 | ret = fsl_counter_clock_enable(fpc); | 332 | ret = fsl_counter_clock_enable(fpc); |
| 339 | mutex_unlock(&fpc->lock); | 333 | mutex_unlock(&fpc->lock); |
| @@ -343,8 +337,6 @@ static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 343 | 337 | ||
| 344 | static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) | 338 | static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) |
| 345 | { | 339 | { |
| 346 | u32 val; | ||
| 347 | |||
| 348 | /* | 340 | /* |
| 349 | * already disabled, do nothing | 341 | * already disabled, do nothing |
| 350 | */ | 342 | */ |
| @@ -356,9 +348,7 @@ static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) | |||
| 356 | return; | 348 | return; |
| 357 | 349 | ||
| 358 | /* no users left, disable PWM counter clock */ | 350 | /* no users left, disable PWM counter clock */ |
| 359 | val = readl(fpc->base + FTM_SC); | 351 | regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0); |
| 360 | val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); | ||
| 361 | writel(val, fpc->base + FTM_SC); | ||
| 362 | 352 | ||
| 363 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); | 353 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); |
| 364 | clk_disable_unprepare(fpc->clk[fpc->cnt_select]); | 354 | clk_disable_unprepare(fpc->clk[fpc->cnt_select]); |
| @@ -370,14 +360,12 @@ static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 370 | u32 val; | 360 | u32 val; |
| 371 | 361 | ||
| 372 | mutex_lock(&fpc->lock); | 362 | mutex_lock(&fpc->lock); |
| 373 | val = readl(fpc->base + FTM_OUTMASK); | 363 | regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), |
| 374 | val |= BIT(pwm->hwpwm); | 364 | BIT(pwm->hwpwm)); |
| 375 | writel(val, fpc->base + FTM_OUTMASK); | ||
| 376 | 365 | ||
| 377 | fsl_counter_clock_disable(fpc); | 366 | fsl_counter_clock_disable(fpc); |
| 378 | 367 | ||
| 379 | val = readl(fpc->base + FTM_OUTMASK); | 368 | regmap_read(fpc->regmap, FTM_OUTMASK, &val); |
| 380 | |||
| 381 | if ((val & 0xFF) == 0xFF) | 369 | if ((val & 0xFF) == 0xFF) |
| 382 | fpc->period_ns = 0; | 370 | fpc->period_ns = 0; |
| 383 | 371 | ||
| @@ -402,19 +390,28 @@ static int fsl_pwm_init(struct fsl_pwm_chip *fpc) | |||
| 402 | if (ret) | 390 | if (ret) |
| 403 | return ret; | 391 | return ret; |
| 404 | 392 | ||
| 405 | writel(0x00, fpc->base + FTM_CNTIN); | 393 | regmap_write(fpc->regmap, FTM_CNTIN, 0x00); |
| 406 | writel(0x00, fpc->base + FTM_OUTINIT); | 394 | regmap_write(fpc->regmap, FTM_OUTINIT, 0x00); |
| 407 | writel(0xFF, fpc->base + FTM_OUTMASK); | 395 | regmap_write(fpc->regmap, FTM_OUTMASK, 0xFF); |
| 408 | 396 | ||
| 409 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); | 397 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); |
| 410 | 398 | ||
| 411 | return 0; | 399 | return 0; |
| 412 | } | 400 | } |
| 413 | 401 | ||
| 402 | static const struct regmap_config fsl_pwm_regmap_config = { | ||
| 403 | .reg_bits = 32, | ||
| 404 | .reg_stride = 4, | ||
| 405 | .val_bits = 32, | ||
| 406 | |||
| 407 | .max_register = FTM_PWMLOAD, | ||
| 408 | }; | ||
| 409 | |||
| 414 | static int fsl_pwm_probe(struct platform_device *pdev) | 410 | static int fsl_pwm_probe(struct platform_device *pdev) |
| 415 | { | 411 | { |
| 416 | struct fsl_pwm_chip *fpc; | 412 | struct fsl_pwm_chip *fpc; |
| 417 | struct resource *res; | 413 | struct resource *res; |
| 414 | void __iomem *base; | ||
| 418 | int ret; | 415 | int ret; |
| 419 | 416 | ||
| 420 | fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); | 417 | fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); |
| @@ -426,9 +423,16 @@ static int fsl_pwm_probe(struct platform_device *pdev) | |||
| 426 | fpc->chip.dev = &pdev->dev; | 423 | fpc->chip.dev = &pdev->dev; |
| 427 | 424 | ||
| 428 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 425 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 429 | fpc->base = devm_ioremap_resource(&pdev->dev, res); | 426 | base = devm_ioremap_resource(&pdev->dev, res); |
| 430 | if (IS_ERR(fpc->base)) | 427 | if (IS_ERR(base)) |
| 431 | return PTR_ERR(fpc->base); | 428 | return PTR_ERR(base); |
| 429 | |||
| 430 | fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, | ||
| 431 | &fsl_pwm_regmap_config); | ||
| 432 | if (IS_ERR(fpc->regmap)) { | ||
| 433 | dev_err(&pdev->dev, "regmap init failed\n"); | ||
| 434 | return PTR_ERR(fpc->regmap); | ||
| 435 | } | ||
| 432 | 436 | ||
| 433 | fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); | 437 | fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); |
| 434 | if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { | 438 | if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { |
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 5449d9150d40..f8b5f109c1ab 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
| 15 | #include <linux/err.h> | 15 | #include <linux/err.h> |
| 16 | #include <linux/clk.h> | 16 | #include <linux/clk.h> |
| 17 | #include <linux/delay.h> | ||
| 17 | #include <linux/io.h> | 18 | #include <linux/io.h> |
| 18 | #include <linux/pwm.h> | 19 | #include <linux/pwm.h> |
| 19 | #include <linux/of.h> | 20 | #include <linux/of.h> |
| @@ -21,24 +22,30 @@ | |||
| 21 | 22 | ||
| 22 | /* i.MX1 and i.MX21 share the same PWM function block: */ | 23 | /* i.MX1 and i.MX21 share the same PWM function block: */ |
| 23 | 24 | ||
| 24 | #define MX1_PWMC 0x00 /* PWM Control Register */ | 25 | #define MX1_PWMC 0x00 /* PWM Control Register */ |
| 25 | #define MX1_PWMS 0x04 /* PWM Sample Register */ | 26 | #define MX1_PWMS 0x04 /* PWM Sample Register */ |
| 26 | #define MX1_PWMP 0x08 /* PWM Period Register */ | 27 | #define MX1_PWMP 0x08 /* PWM Period Register */ |
| 27 | 28 | ||
| 28 | #define MX1_PWMC_EN (1 << 4) | 29 | #define MX1_PWMC_EN (1 << 4) |
| 29 | 30 | ||
| 30 | /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ | 31 | /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ |
| 31 | 32 | ||
| 32 | #define MX3_PWMCR 0x00 /* PWM Control Register */ | 33 | #define MX3_PWMCR 0x00 /* PWM Control Register */ |
| 33 | #define MX3_PWMSAR 0x0C /* PWM Sample Register */ | 34 | #define MX3_PWMSR 0x04 /* PWM Status Register */ |
| 34 | #define MX3_PWMPR 0x10 /* PWM Period Register */ | 35 | #define MX3_PWMSAR 0x0C /* PWM Sample Register */ |
| 35 | #define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4) | 36 | #define MX3_PWMPR 0x10 /* PWM Period Register */ |
| 36 | #define MX3_PWMCR_DOZEEN (1 << 24) | 37 | #define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4) |
| 37 | #define MX3_PWMCR_WAITEN (1 << 23) | 38 | #define MX3_PWMCR_DOZEEN (1 << 24) |
| 39 | #define MX3_PWMCR_WAITEN (1 << 23) | ||
| 38 | #define MX3_PWMCR_DBGEN (1 << 22) | 40 | #define MX3_PWMCR_DBGEN (1 << 22) |
| 39 | #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) | 41 | #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) |
| 40 | #define MX3_PWMCR_CLKSRC_IPG (1 << 16) | 42 | #define MX3_PWMCR_CLKSRC_IPG (1 << 16) |
| 41 | #define MX3_PWMCR_EN (1 << 0) | 43 | #define MX3_PWMCR_SWR (1 << 3) |
| 44 | #define MX3_PWMCR_EN (1 << 0) | ||
| 45 | #define MX3_PWMSR_FIFOAV_4WORDS 0x4 | ||
| 46 | #define MX3_PWMSR_FIFOAV_MASK 0x7 | ||
| 47 | |||
| 48 | #define MX3_PWM_SWR_LOOP 5 | ||
| 42 | 49 | ||
| 43 | struct imx_chip { | 50 | struct imx_chip { |
| 44 | struct clk *clk_per; | 51 | struct clk *clk_per; |
| @@ -103,9 +110,43 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, | |||
| 103 | struct pwm_device *pwm, int duty_ns, int period_ns) | 110 | struct pwm_device *pwm, int duty_ns, int period_ns) |
| 104 | { | 111 | { |
| 105 | struct imx_chip *imx = to_imx_chip(chip); | 112 | struct imx_chip *imx = to_imx_chip(chip); |
| 113 | struct device *dev = chip->dev; | ||
| 106 | unsigned long long c; | 114 | unsigned long long c; |
| 107 | unsigned long period_cycles, duty_cycles, prescale; | 115 | unsigned long period_cycles, duty_cycles, prescale; |
| 108 | u32 cr; | 116 | unsigned int period_ms; |
| 117 | bool enable = test_bit(PWMF_ENABLED, &pwm->flags); | ||
| 118 | int wait_count = 0, fifoav; | ||
| 119 | u32 cr, sr; | ||
| 120 | |||
| 121 | /* | ||
| 122 | * i.MX PWMv2 has a 4-word sample FIFO. | ||
| 123 | * In order to avoid FIFO overflow issue, we do software reset | ||
| 124 | * to clear all sample FIFO if the controller is disabled or | ||
| 125 | * wait for a full PWM cycle to get a relinquished FIFO slot | ||
| 126 | * when the controller is enabled and the FIFO is fully loaded. | ||
| 127 | */ | ||
| 128 | if (enable) { | ||
| 129 | sr = readl(imx->mmio_base + MX3_PWMSR); | ||
| 130 | fifoav = sr & MX3_PWMSR_FIFOAV_MASK; | ||
| 131 | if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) { | ||
| 132 | period_ms = DIV_ROUND_UP(pwm->period, NSEC_PER_MSEC); | ||
| 133 | msleep(period_ms); | ||
| 134 | |||
| 135 | sr = readl(imx->mmio_base + MX3_PWMSR); | ||
| 136 | if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK)) | ||
| 137 | dev_warn(dev, "there is no free FIFO slot\n"); | ||
| 138 | } | ||
| 139 | } else { | ||
| 140 | writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR); | ||
| 141 | do { | ||
| 142 | usleep_range(200, 1000); | ||
| 143 | cr = readl(imx->mmio_base + MX3_PWMCR); | ||
| 144 | } while ((cr & MX3_PWMCR_SWR) && | ||
| 145 | (wait_count++ < MX3_PWM_SWR_LOOP)); | ||
| 146 | |||
| 147 | if (cr & MX3_PWMCR_SWR) | ||
| 148 | dev_warn(dev, "software reset timeout\n"); | ||
| 149 | } | ||
| 109 | 150 | ||
| 110 | c = clk_get_rate(imx->clk_per); | 151 | c = clk_get_rate(imx->clk_per); |
| 111 | c = c * period_ns; | 152 | c = c * period_ns; |
| @@ -135,7 +176,7 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, | |||
| 135 | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | | 176 | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | |
| 136 | MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; | 177 | MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; |
| 137 | 178 | ||
| 138 | if (test_bit(PWMF_ENABLED, &pwm->flags)) | 179 | if (enable) |
| 139 | cr |= MX3_PWMCR_EN; | 180 | cr |= MX3_PWMCR_EN; |
| 140 | 181 | ||
| 141 | writel(cr, imx->mmio_base + MX3_PWMCR); | 182 | writel(cr, imx->mmio_base + MX3_PWMCR); |
diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c new file mode 100644 index 000000000000..cf20d2beacdd --- /dev/null +++ b/drivers/pwm/pwm-lpss-pci.c | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* | ||
| 2 | * Intel Low Power Subsystem PWM controller PCI driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014, Intel Corporation | ||
| 5 | * | ||
| 6 | * Derived from the original pwm-lpss.c | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/pci.h> | ||
| 16 | |||
| 17 | #include "pwm-lpss.h" | ||
| 18 | |||
| 19 | static int pwm_lpss_probe_pci(struct pci_dev *pdev, | ||
| 20 | const struct pci_device_id *id) | ||
| 21 | { | ||
| 22 | const struct pwm_lpss_boardinfo *info; | ||
| 23 | struct pwm_lpss_chip *lpwm; | ||
| 24 | int err; | ||
| 25 | |||
| 26 | err = pcim_enable_device(pdev); | ||
| 27 | if (err < 0) | ||
| 28 | return err; | ||
| 29 | |||
| 30 | info = (struct pwm_lpss_boardinfo *)id->driver_data; | ||
| 31 | lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info); | ||
| 32 | if (IS_ERR(lpwm)) | ||
| 33 | return PTR_ERR(lpwm); | ||
| 34 | |||
| 35 | pci_set_drvdata(pdev, lpwm); | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | static void pwm_lpss_remove_pci(struct pci_dev *pdev) | ||
| 40 | { | ||
| 41 | struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev); | ||
| 42 | |||
| 43 | pwm_lpss_remove(lpwm); | ||
| 44 | } | ||
| 45 | |||
| 46 | static const struct pci_device_id pwm_lpss_pci_ids[] = { | ||
| 47 | { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info}, | ||
| 48 | { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info}, | ||
| 49 | { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info}, | ||
| 50 | { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info}, | ||
| 51 | { }, | ||
| 52 | }; | ||
| 53 | MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids); | ||
| 54 | |||
| 55 | static struct pci_driver pwm_lpss_driver_pci = { | ||
| 56 | .name = "pwm-lpss", | ||
| 57 | .id_table = pwm_lpss_pci_ids, | ||
| 58 | .probe = pwm_lpss_probe_pci, | ||
| 59 | .remove = pwm_lpss_remove_pci, | ||
| 60 | }; | ||
| 61 | module_pci_driver(pwm_lpss_driver_pci); | ||
| 62 | |||
| 63 | MODULE_DESCRIPTION("PWM PCI driver for Intel LPSS"); | ||
| 64 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c new file mode 100644 index 000000000000..18a9c880a76d --- /dev/null +++ b/drivers/pwm/pwm-lpss-platform.c | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | /* | ||
| 2 | * Intel Low Power Subsystem PWM controller driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014, Intel Corporation | ||
| 5 | * | ||
| 6 | * Derived from the original pwm-lpss.c | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/acpi.h> | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | |||
| 18 | #include "pwm-lpss.h" | ||
| 19 | |||
| 20 | static int pwm_lpss_probe_platform(struct platform_device *pdev) | ||
| 21 | { | ||
| 22 | const struct pwm_lpss_boardinfo *info; | ||
| 23 | const struct acpi_device_id *id; | ||
| 24 | struct pwm_lpss_chip *lpwm; | ||
| 25 | struct resource *r; | ||
| 26 | |||
| 27 | id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); | ||
| 28 | if (!id) | ||
| 29 | return -ENODEV; | ||
| 30 | |||
| 31 | info = (const struct pwm_lpss_boardinfo *)id->driver_data; | ||
| 32 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 33 | |||
| 34 | lpwm = pwm_lpss_probe(&pdev->dev, r, info); | ||
| 35 | if (IS_ERR(lpwm)) | ||
| 36 | return PTR_ERR(lpwm); | ||
| 37 | |||
| 38 | platform_set_drvdata(pdev, lpwm); | ||
| 39 | return 0; | ||
| 40 | } | ||
| 41 | |||
| 42 | static int pwm_lpss_remove_platform(struct platform_device *pdev) | ||
| 43 | { | ||
| 44 | struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev); | ||
| 45 | |||
| 46 | return pwm_lpss_remove(lpwm); | ||
| 47 | } | ||
| 48 | |||
| 49 | static const struct acpi_device_id pwm_lpss_acpi_match[] = { | ||
| 50 | { "80860F09", (unsigned long)&pwm_lpss_byt_info }, | ||
| 51 | { "80862288", (unsigned long)&pwm_lpss_bsw_info }, | ||
| 52 | { }, | ||
| 53 | }; | ||
| 54 | MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); | ||
| 55 | |||
| 56 | static struct platform_driver pwm_lpss_driver_platform = { | ||
| 57 | .driver = { | ||
| 58 | .name = "pwm-lpss", | ||
| 59 | .acpi_match_table = pwm_lpss_acpi_match, | ||
| 60 | }, | ||
| 61 | .probe = pwm_lpss_probe_platform, | ||
| 62 | .remove = pwm_lpss_remove_platform, | ||
| 63 | }; | ||
| 64 | module_platform_driver(pwm_lpss_driver_platform); | ||
| 65 | |||
| 66 | MODULE_DESCRIPTION("PWM platform driver for Intel LPSS"); | ||
| 67 | MODULE_LICENSE("GPL v2"); | ||
| 68 | MODULE_ALIAS("platform:pwm-lpss"); | ||
diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 4df994f72d96..e9798253a16f 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c | |||
| @@ -13,15 +13,11 @@ | |||
| 13 | * published by the Free Software Foundation. | 13 | * published by the Free Software Foundation. |
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | #include <linux/acpi.h> | 16 | #include <linux/io.h> |
| 17 | #include <linux/device.h> | ||
| 18 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
| 19 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 20 | #include <linux/pwm.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/pci.h> | ||
| 23 | 19 | ||
| 24 | static int pci_drv, plat_drv; /* So we know which drivers registered */ | 20 | #include "pwm-lpss.h" |
| 25 | 21 | ||
| 26 | #define PWM 0x00000000 | 22 | #define PWM 0x00000000 |
| 27 | #define PWM_ENABLE BIT(31) | 23 | #define PWM_ENABLE BIT(31) |
| @@ -39,14 +35,17 @@ struct pwm_lpss_chip { | |||
| 39 | unsigned long clk_rate; | 35 | unsigned long clk_rate; |
| 40 | }; | 36 | }; |
| 41 | 37 | ||
| 42 | struct pwm_lpss_boardinfo { | 38 | /* BayTrail */ |
| 43 | unsigned long clk_rate; | 39 | const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { |
| 40 | .clk_rate = 25000000 | ||
| 44 | }; | 41 | }; |
| 42 | EXPORT_SYMBOL_GPL(pwm_lpss_byt_info); | ||
| 45 | 43 | ||
| 46 | /* BayTrail */ | 44 | /* Braswell */ |
| 47 | static const struct pwm_lpss_boardinfo byt_info = { | 45 | const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = { |
| 48 | 25000000 | 46 | .clk_rate = 19200000 |
| 49 | }; | 47 | }; |
| 48 | EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info); | ||
| 50 | 49 | ||
| 51 | static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) | 50 | static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) |
| 52 | { | 51 | { |
| @@ -118,9 +117,8 @@ static const struct pwm_ops pwm_lpss_ops = { | |||
| 118 | .owner = THIS_MODULE, | 117 | .owner = THIS_MODULE, |
| 119 | }; | 118 | }; |
| 120 | 119 | ||
| 121 | static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, | 120 | struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, |
| 122 | struct resource *r, | 121 | const struct pwm_lpss_boardinfo *info) |
| 123 | const struct pwm_lpss_boardinfo *info) | ||
| 124 | { | 122 | { |
| 125 | struct pwm_lpss_chip *lpwm; | 123 | struct pwm_lpss_chip *lpwm; |
| 126 | int ret; | 124 | int ret; |
| @@ -147,8 +145,9 @@ static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, | |||
| 147 | 145 | ||
| 148 | return lpwm; | 146 | return lpwm; |
| 149 | } | 147 | } |
| 148 | EXPORT_SYMBOL_GPL(pwm_lpss_probe); | ||
| 150 | 149 | ||
| 151 | static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) | 150 | int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) |
| 152 | { | 151 | { |
| 153 | u32 ctrl; | 152 | u32 ctrl; |
| 154 | 153 | ||
| @@ -157,114 +156,8 @@ static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) | |||
| 157 | 156 | ||
| 158 | return pwmchip_remove(&lpwm->chip); | 157 | return pwmchip_remove(&lpwm->chip); |
| 159 | } | 158 | } |
| 160 | 159 | EXPORT_SYMBOL_GPL(pwm_lpss_remove); | |
| 161 | static int pwm_lpss_probe_pci(struct pci_dev *pdev, | ||
| 162 | const struct pci_device_id *id) | ||
| 163 | { | ||
| 164 | const struct pwm_lpss_boardinfo *info; | ||
| 165 | struct pwm_lpss_chip *lpwm; | ||
| 166 | int err; | ||
| 167 | |||
| 168 | err = pci_enable_device(pdev); | ||
| 169 | if (err < 0) | ||
| 170 | return err; | ||
| 171 | |||
| 172 | info = (struct pwm_lpss_boardinfo *)id->driver_data; | ||
| 173 | lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info); | ||
| 174 | if (IS_ERR(lpwm)) | ||
| 175 | return PTR_ERR(lpwm); | ||
| 176 | |||
| 177 | pci_set_drvdata(pdev, lpwm); | ||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | |||
| 181 | static void pwm_lpss_remove_pci(struct pci_dev *pdev) | ||
| 182 | { | ||
| 183 | struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev); | ||
| 184 | |||
| 185 | pwm_lpss_remove(lpwm); | ||
| 186 | pci_disable_device(pdev); | ||
| 187 | } | ||
| 188 | |||
| 189 | static struct pci_device_id pwm_lpss_pci_ids[] = { | ||
| 190 | { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&byt_info}, | ||
| 191 | { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&byt_info}, | ||
| 192 | { }, | ||
| 193 | }; | ||
| 194 | MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids); | ||
| 195 | |||
| 196 | static struct pci_driver pwm_lpss_driver_pci = { | ||
| 197 | .name = "pwm-lpss", | ||
| 198 | .id_table = pwm_lpss_pci_ids, | ||
| 199 | .probe = pwm_lpss_probe_pci, | ||
| 200 | .remove = pwm_lpss_remove_pci, | ||
| 201 | }; | ||
| 202 | |||
| 203 | static int pwm_lpss_probe_platform(struct platform_device *pdev) | ||
| 204 | { | ||
| 205 | const struct pwm_lpss_boardinfo *info; | ||
| 206 | const struct acpi_device_id *id; | ||
| 207 | struct pwm_lpss_chip *lpwm; | ||
| 208 | struct resource *r; | ||
| 209 | |||
| 210 | id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); | ||
| 211 | if (!id) | ||
| 212 | return -ENODEV; | ||
| 213 | |||
| 214 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 215 | |||
| 216 | info = (struct pwm_lpss_boardinfo *)id->driver_data; | ||
| 217 | lpwm = pwm_lpss_probe(&pdev->dev, r, info); | ||
| 218 | if (IS_ERR(lpwm)) | ||
| 219 | return PTR_ERR(lpwm); | ||
| 220 | |||
| 221 | platform_set_drvdata(pdev, lpwm); | ||
| 222 | return 0; | ||
| 223 | } | ||
| 224 | |||
| 225 | static int pwm_lpss_remove_platform(struct platform_device *pdev) | ||
| 226 | { | ||
| 227 | struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev); | ||
| 228 | |||
| 229 | return pwm_lpss_remove(lpwm); | ||
| 230 | } | ||
| 231 | |||
| 232 | static const struct acpi_device_id pwm_lpss_acpi_match[] = { | ||
| 233 | { "80860F09", (unsigned long)&byt_info }, | ||
| 234 | { }, | ||
| 235 | }; | ||
| 236 | MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); | ||
| 237 | |||
| 238 | static struct platform_driver pwm_lpss_driver_platform = { | ||
| 239 | .driver = { | ||
| 240 | .name = "pwm-lpss", | ||
| 241 | .acpi_match_table = pwm_lpss_acpi_match, | ||
| 242 | }, | ||
| 243 | .probe = pwm_lpss_probe_platform, | ||
| 244 | .remove = pwm_lpss_remove_platform, | ||
| 245 | }; | ||
| 246 | |||
| 247 | static int __init pwm_init(void) | ||
| 248 | { | ||
| 249 | pci_drv = pci_register_driver(&pwm_lpss_driver_pci); | ||
| 250 | plat_drv = platform_driver_register(&pwm_lpss_driver_platform); | ||
| 251 | if (pci_drv && plat_drv) | ||
| 252 | return pci_drv; | ||
| 253 | |||
| 254 | return 0; | ||
| 255 | } | ||
| 256 | module_init(pwm_init); | ||
| 257 | |||
| 258 | static void __exit pwm_exit(void) | ||
| 259 | { | ||
| 260 | if (!pci_drv) | ||
| 261 | pci_unregister_driver(&pwm_lpss_driver_pci); | ||
| 262 | if (!plat_drv) | ||
| 263 | platform_driver_unregister(&pwm_lpss_driver_platform); | ||
| 264 | } | ||
| 265 | module_exit(pwm_exit); | ||
| 266 | 160 | ||
| 267 | MODULE_DESCRIPTION("PWM driver for Intel LPSS"); | 161 | MODULE_DESCRIPTION("PWM driver for Intel LPSS"); |
| 268 | MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); | 162 | MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); |
| 269 | MODULE_LICENSE("GPL v2"); | 163 | MODULE_LICENSE("GPL v2"); |
| 270 | MODULE_ALIAS("platform:pwm-lpss"); | ||
diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h new file mode 100644 index 000000000000..aa041bb1b67d --- /dev/null +++ b/drivers/pwm/pwm-lpss.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* | ||
| 2 | * Intel Low Power Subsystem PWM controller driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014, Intel Corporation | ||
| 5 | * | ||
| 6 | * Derived from the original pwm-lpss.c | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef __PWM_LPSS_H | ||
| 14 | #define __PWM_LPSS_H | ||
| 15 | |||
| 16 | #include <linux/device.h> | ||
| 17 | #include <linux/pwm.h> | ||
| 18 | |||
| 19 | struct pwm_lpss_chip; | ||
| 20 | |||
| 21 | struct pwm_lpss_boardinfo { | ||
| 22 | unsigned long clk_rate; | ||
| 23 | }; | ||
| 24 | |||
| 25 | extern const struct pwm_lpss_boardinfo pwm_lpss_byt_info; | ||
| 26 | extern const struct pwm_lpss_boardinfo pwm_lpss_bsw_info; | ||
| 27 | |||
| 28 | struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, | ||
| 29 | const struct pwm_lpss_boardinfo *info); | ||
| 30 | int pwm_lpss_remove(struct pwm_lpss_chip *lpwm); | ||
| 31 | |||
| 32 | #endif /* __PWM_LPSS_H */ | ||
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index bdd8644c01cf..9442df244101 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c | |||
| @@ -24,7 +24,9 @@ | |||
| 24 | #define PWM_ENABLE (1 << 0) | 24 | #define PWM_ENABLE (1 << 0) |
| 25 | #define PWM_CONTINUOUS (1 << 1) | 25 | #define PWM_CONTINUOUS (1 << 1) |
| 26 | #define PWM_DUTY_POSITIVE (1 << 3) | 26 | #define PWM_DUTY_POSITIVE (1 << 3) |
| 27 | #define PWM_DUTY_NEGATIVE (0 << 3) | ||
| 27 | #define PWM_INACTIVE_NEGATIVE (0 << 4) | 28 | #define PWM_INACTIVE_NEGATIVE (0 << 4) |
| 29 | #define PWM_INACTIVE_POSITIVE (1 << 4) | ||
| 28 | #define PWM_OUTPUT_LEFT (0 << 5) | 30 | #define PWM_OUTPUT_LEFT (0 << 5) |
| 29 | #define PWM_LP_DISABLE (0 << 8) | 31 | #define PWM_LP_DISABLE (0 << 8) |
| 30 | 32 | ||
| @@ -45,8 +47,10 @@ struct rockchip_pwm_regs { | |||
| 45 | struct rockchip_pwm_data { | 47 | struct rockchip_pwm_data { |
| 46 | struct rockchip_pwm_regs regs; | 48 | struct rockchip_pwm_regs regs; |
| 47 | unsigned int prescaler; | 49 | unsigned int prescaler; |
| 50 | const struct pwm_ops *ops; | ||
| 48 | 51 | ||
| 49 | void (*set_enable)(struct pwm_chip *chip, bool enable); | 52 | void (*set_enable)(struct pwm_chip *chip, |
| 53 | struct pwm_device *pwm, bool enable); | ||
| 50 | }; | 54 | }; |
| 51 | 55 | ||
| 52 | static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) | 56 | static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) |
| @@ -54,7 +58,8 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) | |||
| 54 | return container_of(c, struct rockchip_pwm_chip, chip); | 58 | return container_of(c, struct rockchip_pwm_chip, chip); |
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) | 61 | static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, |
| 62 | struct pwm_device *pwm, bool enable) | ||
| 58 | { | 63 | { |
| 59 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 64 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
| 60 | u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; | 65 | u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; |
| @@ -70,14 +75,19 @@ static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) | |||
| 70 | writel_relaxed(val, pc->base + pc->data->regs.ctrl); | 75 | writel_relaxed(val, pc->base + pc->data->regs.ctrl); |
| 71 | } | 76 | } |
| 72 | 77 | ||
| 73 | static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) | 78 | static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, |
| 79 | struct pwm_device *pwm, bool enable) | ||
| 74 | { | 80 | { |
| 75 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 81 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
| 76 | u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | | 82 | u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | |
| 77 | PWM_CONTINUOUS | PWM_DUTY_POSITIVE | | 83 | PWM_CONTINUOUS; |
| 78 | PWM_INACTIVE_NEGATIVE; | ||
| 79 | u32 val; | 84 | u32 val; |
| 80 | 85 | ||
| 86 | if (pwm->polarity == PWM_POLARITY_INVERSED) | ||
| 87 | enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; | ||
| 88 | else | ||
| 89 | enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; | ||
| 90 | |||
| 81 | val = readl_relaxed(pc->base + pc->data->regs.ctrl); | 91 | val = readl_relaxed(pc->base + pc->data->regs.ctrl); |
| 82 | 92 | ||
| 83 | if (enable) | 93 | if (enable) |
| @@ -124,6 +134,19 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 124 | return 0; | 134 | return 0; |
| 125 | } | 135 | } |
| 126 | 136 | ||
| 137 | static int rockchip_pwm_set_polarity(struct pwm_chip *chip, | ||
| 138 | struct pwm_device *pwm, | ||
| 139 | enum pwm_polarity polarity) | ||
| 140 | { | ||
| 141 | /* | ||
| 142 | * No action needed here because pwm->polarity will be set by the core | ||
| 143 | * and the core will only change polarity when the PWM is not enabled. | ||
| 144 | * We'll handle things in set_enable(). | ||
| 145 | */ | ||
| 146 | |||
| 147 | return 0; | ||
| 148 | } | ||
| 149 | |||
| 127 | static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | 150 | static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
| 128 | { | 151 | { |
| 129 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 152 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
| @@ -133,7 +156,7 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 133 | if (ret) | 156 | if (ret) |
| 134 | return ret; | 157 | return ret; |
| 135 | 158 | ||
| 136 | pc->data->set_enable(chip, true); | 159 | pc->data->set_enable(chip, pwm, true); |
| 137 | 160 | ||
| 138 | return 0; | 161 | return 0; |
| 139 | } | 162 | } |
| @@ -142,18 +165,26 @@ static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 142 | { | 165 | { |
| 143 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 166 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
| 144 | 167 | ||
| 145 | pc->data->set_enable(chip, false); | 168 | pc->data->set_enable(chip, pwm, false); |
| 146 | 169 | ||
| 147 | clk_disable(pc->clk); | 170 | clk_disable(pc->clk); |
| 148 | } | 171 | } |
| 149 | 172 | ||
| 150 | static const struct pwm_ops rockchip_pwm_ops = { | 173 | static const struct pwm_ops rockchip_pwm_ops_v1 = { |
| 151 | .config = rockchip_pwm_config, | 174 | .config = rockchip_pwm_config, |
| 152 | .enable = rockchip_pwm_enable, | 175 | .enable = rockchip_pwm_enable, |
| 153 | .disable = rockchip_pwm_disable, | 176 | .disable = rockchip_pwm_disable, |
| 154 | .owner = THIS_MODULE, | 177 | .owner = THIS_MODULE, |
| 155 | }; | 178 | }; |
| 156 | 179 | ||
| 180 | static const struct pwm_ops rockchip_pwm_ops_v2 = { | ||
| 181 | .config = rockchip_pwm_config, | ||
| 182 | .set_polarity = rockchip_pwm_set_polarity, | ||
| 183 | .enable = rockchip_pwm_enable, | ||
| 184 | .disable = rockchip_pwm_disable, | ||
| 185 | .owner = THIS_MODULE, | ||
| 186 | }; | ||
| 187 | |||
| 157 | static const struct rockchip_pwm_data pwm_data_v1 = { | 188 | static const struct rockchip_pwm_data pwm_data_v1 = { |
| 158 | .regs = { | 189 | .regs = { |
| 159 | .duty = 0x04, | 190 | .duty = 0x04, |
| @@ -162,6 +193,7 @@ static const struct rockchip_pwm_data pwm_data_v1 = { | |||
| 162 | .ctrl = 0x0c, | 193 | .ctrl = 0x0c, |
| 163 | }, | 194 | }, |
| 164 | .prescaler = 2, | 195 | .prescaler = 2, |
| 196 | .ops = &rockchip_pwm_ops_v1, | ||
| 165 | .set_enable = rockchip_pwm_set_enable_v1, | 197 | .set_enable = rockchip_pwm_set_enable_v1, |
| 166 | }; | 198 | }; |
| 167 | 199 | ||
| @@ -173,6 +205,7 @@ static const struct rockchip_pwm_data pwm_data_v2 = { | |||
| 173 | .ctrl = 0x0c, | 205 | .ctrl = 0x0c, |
| 174 | }, | 206 | }, |
| 175 | .prescaler = 1, | 207 | .prescaler = 1, |
| 208 | .ops = &rockchip_pwm_ops_v2, | ||
| 176 | .set_enable = rockchip_pwm_set_enable_v2, | 209 | .set_enable = rockchip_pwm_set_enable_v2, |
| 177 | }; | 210 | }; |
| 178 | 211 | ||
| @@ -184,6 +217,7 @@ static const struct rockchip_pwm_data pwm_data_vop = { | |||
| 184 | .ctrl = 0x00, | 217 | .ctrl = 0x00, |
| 185 | }, | 218 | }, |
| 186 | .prescaler = 1, | 219 | .prescaler = 1, |
| 220 | .ops = &rockchip_pwm_ops_v2, | ||
| 187 | .set_enable = rockchip_pwm_set_enable_v2, | 221 | .set_enable = rockchip_pwm_set_enable_v2, |
| 188 | }; | 222 | }; |
| 189 | 223 | ||
| @@ -227,10 +261,15 @@ static int rockchip_pwm_probe(struct platform_device *pdev) | |||
| 227 | 261 | ||
| 228 | pc->data = id->data; | 262 | pc->data = id->data; |
| 229 | pc->chip.dev = &pdev->dev; | 263 | pc->chip.dev = &pdev->dev; |
| 230 | pc->chip.ops = &rockchip_pwm_ops; | 264 | pc->chip.ops = pc->data->ops; |
| 231 | pc->chip.base = -1; | 265 | pc->chip.base = -1; |
| 232 | pc->chip.npwm = 1; | 266 | pc->chip.npwm = 1; |
| 233 | 267 | ||
| 268 | if (pc->data->ops->set_polarity) { | ||
| 269 | pc->chip.of_xlate = of_pwm_xlate_with_flags; | ||
| 270 | pc->chip.of_pwm_n_cells = 3; | ||
| 271 | } | ||
| 272 | |||
| 234 | ret = pwmchip_add(&pc->chip); | 273 | ret = pwmchip_add(&pc->chip); |
| 235 | if (ret < 0) { | 274 | if (ret < 0) { |
| 236 | clk_unprepare(pc->clk); | 275 | clk_unprepare(pc->clk); |
