diff options
| -rw-r--r-- | Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt | 29 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt | 30 | ||||
| -rw-r--r-- | drivers/pwm/Kconfig | 22 | ||||
| -rw-r--r-- | drivers/pwm/Makefile | 2 | ||||
| -rw-r--r-- | drivers/pwm/pwm-atmel-hlcdc.c | 299 | ||||
| -rw-r--r-- | drivers/pwm/pwm-bcm2835.c | 205 | ||||
| -rw-r--r-- | drivers/pwm/pwm-fsl-ftm.c | 64 |
7 files changed, 646 insertions, 5 deletions
diff --git a/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt new file mode 100644 index 000000000000..cfda0d57d302 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | Device-Tree bindings for Atmel's HLCDC (High-end LCD Controller) PWM driver | ||
| 2 | |||
| 3 | The Atmel HLCDC PWM is subdevice of the HLCDC MFD device. | ||
| 4 | See ../mfd/atmel-hlcdc.txt for more details. | ||
| 5 | |||
| 6 | Required properties: | ||
| 7 | - compatible: value should be one of the following: | ||
| 8 | "atmel,hlcdc-pwm" | ||
| 9 | - pinctr-names: the pin control state names. Should contain "default". | ||
| 10 | - pinctrl-0: should contain the pinctrl states described by pinctrl | ||
| 11 | default. | ||
| 12 | - #pwm-cells: should be set to 3. This PWM chip use the default 3 cells | ||
| 13 | bindings defined in pwm.txt in this directory. | ||
| 14 | |||
| 15 | Example: | ||
| 16 | |||
| 17 | hlcdc: hlcdc@f0030000 { | ||
| 18 | compatible = "atmel,sama5d3-hlcdc"; | ||
| 19 | reg = <0xf0030000 0x2000>; | ||
| 20 | clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>; | ||
| 21 | clock-names = "periph_clk","sys_clk", "slow_clk"; | ||
| 22 | |||
| 23 | hlcdc_pwm: hlcdc-pwm { | ||
| 24 | compatible = "atmel,hlcdc-pwm"; | ||
| 25 | pinctrl-names = "default"; | ||
| 26 | pinctrl-0 = <&pinctrl_lcd_pwm>; | ||
| 27 | #pwm-cells = <3>; | ||
| 28 | }; | ||
| 29 | }; | ||
diff --git a/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt b/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt new file mode 100644 index 000000000000..fb6fb31bc4c4 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | BCM2835 PWM controller (Raspberry Pi controller) | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: should be "brcm,bcm2835-pwm" | ||
| 5 | - reg: physical base address and length of the controller's registers | ||
| 6 | - clock: This clock defines the base clock frequency of the PWM hardware | ||
| 7 | system, the period and the duty_cycle of the PWM signal is a multiple of | ||
| 8 | the base period. | ||
| 9 | - #pwm-cells: Should be 2. See pwm.txt in this directory for a description of | ||
| 10 | the cells format. | ||
| 11 | |||
| 12 | Examples: | ||
| 13 | |||
| 14 | pwm@2020c000 { | ||
| 15 | compatible = "brcm,bcm2835-pwm"; | ||
| 16 | reg = <0x2020c000 0x28>; | ||
| 17 | clocks = <&clk_pwm>; | ||
| 18 | #pwm-cells = <2>; | ||
| 19 | }; | ||
| 20 | |||
| 21 | clocks { | ||
| 22 | .... | ||
| 23 | clk_pwm: pwm { | ||
| 24 | compatible = "fixed-clock"; | ||
| 25 | reg = <3>; | ||
| 26 | #clock-cells = <0>; | ||
| 27 | clock-frequency = <9200000>; | ||
| 28 | }; | ||
| 29 | .... | ||
| 30 | }; | ||
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index ef2dd2e4754b..a3ecf5809634 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
| @@ -50,6 +50,17 @@ config PWM_ATMEL | |||
| 50 | To compile this driver as a module, choose M here: the module | 50 | To compile this driver as a module, choose M here: the module |
| 51 | will be called pwm-atmel. | 51 | will be called pwm-atmel. |
| 52 | 52 | ||
| 53 | config PWM_ATMEL_HLCDC_PWM | ||
| 54 | tristate "Atmel HLCDC PWM support" | ||
| 55 | depends on MFD_ATMEL_HLCDC | ||
| 56 | help | ||
| 57 | Generic PWM framework driver for the PWM output of the HLCDC | ||
| 58 | (Atmel High-end LCD Controller). This PWM output is mainly used | ||
| 59 | to control the LCD backlight. | ||
| 60 | |||
| 61 | To compile this driver as a module, choose M here: the module | ||
| 62 | will be called pwm-atmel-hlcdc. | ||
| 63 | |||
| 53 | config PWM_ATMEL_TCB | 64 | config PWM_ATMEL_TCB |
| 54 | tristate "Atmel TC Block PWM support" | 65 | tristate "Atmel TC Block PWM support" |
| 55 | depends on ATMEL_TCLIB && OF | 66 | depends on ATMEL_TCLIB && OF |
| @@ -71,6 +82,15 @@ config PWM_BCM_KONA | |||
| 71 | To compile this driver as a module, choose M here: the module | 82 | To compile this driver as a module, choose M here: the module |
| 72 | will be called pwm-bcm-kona. | 83 | will be called pwm-bcm-kona. |
| 73 | 84 | ||
| 85 | config PWM_BCM2835 | ||
| 86 | tristate "BCM2835 PWM support" | ||
| 87 | depends on ARCH_BCM2835 | ||
| 88 | help | ||
| 89 | PWM framework driver for BCM2835 controller (Raspberry Pi) | ||
| 90 | |||
| 91 | To compile this driver as a module, choose M here: the module | ||
| 92 | will be called pwm-bcm2835. | ||
| 93 | |||
| 74 | config PWM_BFIN | 94 | config PWM_BFIN |
| 75 | tristate "Blackfin PWM support" | 95 | tristate "Blackfin PWM support" |
| 76 | depends on BFIN_GPTIMERS | 96 | depends on BFIN_GPTIMERS |
| @@ -235,7 +255,7 @@ config PWM_ROCKCHIP | |||
| 235 | 255 | ||
| 236 | config PWM_SAMSUNG | 256 | config PWM_SAMSUNG |
| 237 | tristate "Samsung PWM support" | 257 | tristate "Samsung PWM support" |
| 238 | depends on PLAT_SAMSUNG | 258 | depends on PLAT_SAMSUNG || ARCH_EXYNOS |
| 239 | help | 259 | help |
| 240 | Generic PWM framework driver for Samsung. | 260 | Generic PWM framework driver for Samsung. |
| 241 | 261 | ||
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index c458606c3755..65259ac1e8de 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
| @@ -2,8 +2,10 @@ obj-$(CONFIG_PWM) += core.o | |||
| 2 | obj-$(CONFIG_PWM_SYSFS) += sysfs.o | 2 | obj-$(CONFIG_PWM_SYSFS) += sysfs.o |
| 3 | obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o | 3 | obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o |
| 4 | obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o | 4 | obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o |
| 5 | obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o | ||
| 5 | obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o | 6 | obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o |
| 6 | obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o | 7 | obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o |
| 8 | obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o | ||
| 7 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o | 9 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o |
| 8 | obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o | 10 | obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o |
| 9 | obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o | 11 | obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o |
diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c new file mode 100644 index 000000000000..e7a785fadcdf --- /dev/null +++ b/drivers/pwm/pwm-atmel-hlcdc.c | |||
| @@ -0,0 +1,299 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 Free Electrons | ||
| 3 | * Copyright (C) 2014 Atmel | ||
| 4 | * | ||
| 5 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms of the GNU General Public License version 2 as published by | ||
| 9 | * the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 14 | * more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License along with | ||
| 17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/clk.h> | ||
| 21 | #include <linux/delay.h> | ||
| 22 | #include <linux/mfd/atmel-hlcdc.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | #include <linux/pwm.h> | ||
| 26 | #include <linux/regmap.h> | ||
| 27 | |||
| 28 | #define ATMEL_HLCDC_PWMCVAL_MASK GENMASK(15, 8) | ||
| 29 | #define ATMEL_HLCDC_PWMCVAL(x) (((x) << 8) & ATMEL_HLCDC_PWMCVAL_MASK) | ||
| 30 | #define ATMEL_HLCDC_PWMPOL BIT(4) | ||
| 31 | #define ATMEL_HLCDC_PWMPS_MASK GENMASK(2, 0) | ||
| 32 | #define ATMEL_HLCDC_PWMPS_MAX 0x6 | ||
| 33 | #define ATMEL_HLCDC_PWMPS(x) ((x) & ATMEL_HLCDC_PWMPS_MASK) | ||
| 34 | |||
| 35 | struct atmel_hlcdc_pwm_errata { | ||
| 36 | bool slow_clk_erratum; | ||
| 37 | bool div1_clk_erratum; | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct atmel_hlcdc_pwm { | ||
| 41 | struct pwm_chip chip; | ||
| 42 | struct atmel_hlcdc *hlcdc; | ||
| 43 | struct clk *cur_clk; | ||
| 44 | const struct atmel_hlcdc_pwm_errata *errata; | ||
| 45 | }; | ||
| 46 | |||
| 47 | static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip) | ||
| 48 | { | ||
| 49 | return container_of(chip, struct atmel_hlcdc_pwm, chip); | ||
| 50 | } | ||
| 51 | |||
| 52 | static int atmel_hlcdc_pwm_config(struct pwm_chip *c, | ||
| 53 | struct pwm_device *pwm, | ||
| 54 | int duty_ns, int period_ns) | ||
| 55 | { | ||
| 56 | struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); | ||
| 57 | struct atmel_hlcdc *hlcdc = chip->hlcdc; | ||
| 58 | struct clk *new_clk = hlcdc->slow_clk; | ||
| 59 | u64 pwmcval = duty_ns * 256; | ||
| 60 | unsigned long clk_freq; | ||
| 61 | u64 clk_period_ns; | ||
| 62 | u32 pwmcfg; | ||
| 63 | int pres; | ||
| 64 | |||
| 65 | if (!chip->errata || !chip->errata->slow_clk_erratum) { | ||
| 66 | clk_freq = clk_get_rate(new_clk); | ||
| 67 | clk_period_ns = (u64)NSEC_PER_SEC * 256; | ||
| 68 | do_div(clk_period_ns, clk_freq); | ||
| 69 | } | ||
| 70 | |||
| 71 | /* Errata: cannot use slow clk on some IP revisions */ | ||
| 72 | if ((chip->errata && chip->errata->slow_clk_erratum) || | ||
| 73 | clk_period_ns > period_ns) { | ||
| 74 | new_clk = hlcdc->sys_clk; | ||
| 75 | clk_freq = clk_get_rate(new_clk); | ||
| 76 | clk_period_ns = (u64)NSEC_PER_SEC * 256; | ||
| 77 | do_div(clk_period_ns, clk_freq); | ||
| 78 | } | ||
| 79 | |||
| 80 | for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) { | ||
| 81 | /* Errata: cannot divide by 1 on some IP revisions */ | ||
| 82 | if (!pres && chip->errata && chip->errata->div1_clk_erratum) | ||
| 83 | continue; | ||
| 84 | |||
| 85 | if ((clk_period_ns << pres) >= period_ns) | ||
| 86 | break; | ||
| 87 | } | ||
| 88 | |||
| 89 | if (pres > ATMEL_HLCDC_PWMPS_MAX) | ||
| 90 | return -EINVAL; | ||
| 91 | |||
| 92 | pwmcfg = ATMEL_HLCDC_PWMPS(pres); | ||
| 93 | |||
| 94 | if (new_clk != chip->cur_clk) { | ||
| 95 | u32 gencfg = 0; | ||
| 96 | int ret; | ||
| 97 | |||
| 98 | ret = clk_prepare_enable(new_clk); | ||
| 99 | if (ret) | ||
| 100 | return ret; | ||
| 101 | |||
| 102 | clk_disable_unprepare(chip->cur_clk); | ||
| 103 | chip->cur_clk = new_clk; | ||
| 104 | |||
| 105 | if (new_clk == hlcdc->sys_clk) | ||
| 106 | gencfg = ATMEL_HLCDC_CLKPWMSEL; | ||
| 107 | |||
| 108 | ret = regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(0), | ||
| 109 | ATMEL_HLCDC_CLKPWMSEL, gencfg); | ||
| 110 | if (ret) | ||
| 111 | return ret; | ||
| 112 | } | ||
| 113 | |||
| 114 | do_div(pwmcval, period_ns); | ||
| 115 | |||
| 116 | /* | ||
| 117 | * The PWM duty cycle is configurable from 0/256 to 255/256 of the | ||
| 118 | * period cycle. Hence we can't set a duty cycle occupying the | ||
| 119 | * whole period cycle if we're asked to. | ||
| 120 | * Set it to 255 if pwmcval is greater than 256. | ||
| 121 | */ | ||
| 122 | if (pwmcval > 255) | ||
| 123 | pwmcval = 255; | ||
| 124 | |||
| 125 | pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval); | ||
| 126 | |||
| 127 | return regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6), | ||
| 128 | ATMEL_HLCDC_PWMCVAL_MASK | | ||
| 129 | ATMEL_HLCDC_PWMPS_MASK, | ||
| 130 | pwmcfg); | ||
| 131 | } | ||
| 132 | |||
| 133 | static int atmel_hlcdc_pwm_set_polarity(struct pwm_chip *c, | ||
| 134 | struct pwm_device *pwm, | ||
| 135 | enum pwm_polarity polarity) | ||
| 136 | { | ||
| 137 | struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); | ||
| 138 | struct atmel_hlcdc *hlcdc = chip->hlcdc; | ||
| 139 | u32 cfg = 0; | ||
| 140 | |||
| 141 | if (polarity == PWM_POLARITY_NORMAL) | ||
| 142 | cfg = ATMEL_HLCDC_PWMPOL; | ||
| 143 | |||
| 144 | return regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6), | ||
| 145 | ATMEL_HLCDC_PWMPOL, cfg); | ||
| 146 | } | ||
| 147 | |||
| 148 | static int atmel_hlcdc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) | ||
| 149 | { | ||
| 150 | struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); | ||
| 151 | struct atmel_hlcdc *hlcdc = chip->hlcdc; | ||
| 152 | u32 status; | ||
| 153 | int ret; | ||
| 154 | |||
| 155 | ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PWM); | ||
| 156 | if (ret) | ||
| 157 | return ret; | ||
| 158 | |||
| 159 | while (true) { | ||
| 160 | ret = regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status); | ||
| 161 | if (ret) | ||
| 162 | return ret; | ||
| 163 | |||
| 164 | if ((status & ATMEL_HLCDC_PWM) != 0) | ||
| 165 | break; | ||
| 166 | |||
| 167 | usleep_range(1, 10); | ||
| 168 | } | ||
| 169 | |||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | static void atmel_hlcdc_pwm_disable(struct pwm_chip *c, | ||
| 174 | struct pwm_device *pwm) | ||
| 175 | { | ||
| 176 | struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); | ||
| 177 | struct atmel_hlcdc *hlcdc = chip->hlcdc; | ||
| 178 | u32 status; | ||
| 179 | int ret; | ||
| 180 | |||
| 181 | ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PWM); | ||
| 182 | if (ret) | ||
| 183 | return; | ||
| 184 | |||
| 185 | while (true) { | ||
| 186 | ret = regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status); | ||
| 187 | if (ret) | ||
| 188 | return; | ||
| 189 | |||
| 190 | if ((status & ATMEL_HLCDC_PWM) == 0) | ||
| 191 | break; | ||
| 192 | |||
| 193 | usleep_range(1, 10); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | static const struct pwm_ops atmel_hlcdc_pwm_ops = { | ||
| 198 | .config = atmel_hlcdc_pwm_config, | ||
| 199 | .set_polarity = atmel_hlcdc_pwm_set_polarity, | ||
| 200 | .enable = atmel_hlcdc_pwm_enable, | ||
| 201 | .disable = atmel_hlcdc_pwm_disable, | ||
| 202 | .owner = THIS_MODULE, | ||
| 203 | }; | ||
| 204 | |||
| 205 | static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_at91sam9x5_errata = { | ||
| 206 | .slow_clk_erratum = true, | ||
| 207 | }; | ||
| 208 | |||
| 209 | static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = { | ||
| 210 | .div1_clk_erratum = true, | ||
| 211 | }; | ||
| 212 | |||
| 213 | static const struct of_device_id atmel_hlcdc_dt_ids[] = { | ||
| 214 | { | ||
| 215 | .compatible = "atmel,at91sam9x5-hlcdc", | ||
| 216 | .data = &atmel_hlcdc_pwm_at91sam9x5_errata, | ||
| 217 | }, | ||
| 218 | { | ||
| 219 | .compatible = "atmel,sama5d3-hlcdc", | ||
| 220 | .data = &atmel_hlcdc_pwm_sama5d3_errata, | ||
| 221 | }, | ||
| 222 | { /* sentinel */ }, | ||
| 223 | }; | ||
| 224 | |||
| 225 | static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) | ||
| 226 | { | ||
| 227 | const struct of_device_id *match; | ||
| 228 | struct device *dev = &pdev->dev; | ||
| 229 | struct atmel_hlcdc_pwm *chip; | ||
| 230 | struct atmel_hlcdc *hlcdc; | ||
| 231 | int ret; | ||
| 232 | |||
| 233 | hlcdc = dev_get_drvdata(dev->parent); | ||
| 234 | |||
| 235 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); | ||
| 236 | if (!chip) | ||
| 237 | return -ENOMEM; | ||
| 238 | |||
| 239 | ret = clk_prepare_enable(hlcdc->periph_clk); | ||
| 240 | if (ret) | ||
| 241 | return ret; | ||
| 242 | |||
| 243 | match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node); | ||
| 244 | if (match) | ||
| 245 | chip->errata = match->data; | ||
| 246 | |||
| 247 | chip->hlcdc = hlcdc; | ||
| 248 | chip->chip.ops = &atmel_hlcdc_pwm_ops; | ||
| 249 | chip->chip.dev = dev; | ||
| 250 | chip->chip.base = -1; | ||
| 251 | chip->chip.npwm = 1; | ||
| 252 | chip->chip.of_xlate = of_pwm_xlate_with_flags; | ||
| 253 | chip->chip.of_pwm_n_cells = 3; | ||
| 254 | chip->chip.can_sleep = 1; | ||
| 255 | |||
| 256 | ret = pwmchip_add(&chip->chip); | ||
| 257 | if (ret) { | ||
| 258 | clk_disable_unprepare(hlcdc->periph_clk); | ||
| 259 | return ret; | ||
| 260 | } | ||
| 261 | |||
| 262 | platform_set_drvdata(pdev, chip); | ||
| 263 | |||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | static int atmel_hlcdc_pwm_remove(struct platform_device *pdev) | ||
| 268 | { | ||
| 269 | struct atmel_hlcdc_pwm *chip = platform_get_drvdata(pdev); | ||
| 270 | int ret; | ||
| 271 | |||
| 272 | ret = pwmchip_remove(&chip->chip); | ||
| 273 | if (ret) | ||
| 274 | return ret; | ||
| 275 | |||
| 276 | clk_disable_unprepare(chip->hlcdc->periph_clk); | ||
| 277 | |||
| 278 | return 0; | ||
| 279 | } | ||
| 280 | |||
| 281 | static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = { | ||
| 282 | { .compatible = "atmel,hlcdc-pwm" }, | ||
| 283 | { /* sentinel */ }, | ||
| 284 | }; | ||
| 285 | |||
| 286 | static struct platform_driver atmel_hlcdc_pwm_driver = { | ||
| 287 | .driver = { | ||
| 288 | .name = "atmel-hlcdc-pwm", | ||
| 289 | .of_match_table = atmel_hlcdc_pwm_dt_ids, | ||
| 290 | }, | ||
| 291 | .probe = atmel_hlcdc_pwm_probe, | ||
| 292 | .remove = atmel_hlcdc_pwm_remove, | ||
| 293 | }; | ||
| 294 | module_platform_driver(atmel_hlcdc_pwm_driver); | ||
| 295 | |||
| 296 | MODULE_ALIAS("platform:atmel-hlcdc-pwm"); | ||
| 297 | MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); | ||
| 298 | MODULE_DESCRIPTION("Atmel HLCDC PWM driver"); | ||
| 299 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c new file mode 100644 index 000000000000..b4c7f956b6fa --- /dev/null +++ b/drivers/pwm/pwm-bcm2835.c | |||
| @@ -0,0 +1,205 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2014 Bart Tanghe <bart.tanghe@thomasmore.be> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License as published by | ||
| 6 | * the Free Software Foundation; version 2. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/clk.h> | ||
| 10 | #include <linux/err.h> | ||
| 11 | #include <linux/io.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/of.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/pwm.h> | ||
| 16 | |||
| 17 | #define PWM_CONTROL 0x000 | ||
| 18 | #define PWM_CONTROL_SHIFT(x) ((x) * 8) | ||
| 19 | #define PWM_CONTROL_MASK 0xff | ||
| 20 | #define PWM_MODE 0x80 /* set timer in PWM mode */ | ||
| 21 | #define PWM_ENABLE (1 << 0) | ||
| 22 | #define PWM_POLARITY (1 << 4) | ||
| 23 | |||
| 24 | #define PERIOD(x) (((x) * 0x10) + 0x10) | ||
| 25 | #define DUTY(x) (((x) * 0x10) + 0x14) | ||
| 26 | |||
| 27 | #define MIN_PERIOD 108 /* 9.2 MHz max. PWM clock */ | ||
| 28 | |||
| 29 | struct bcm2835_pwm { | ||
| 30 | struct pwm_chip chip; | ||
| 31 | struct device *dev; | ||
| 32 | unsigned long scaler; | ||
| 33 | void __iomem *base; | ||
| 34 | struct clk *clk; | ||
| 35 | }; | ||
| 36 | |||
| 37 | static inline struct bcm2835_pwm *to_bcm2835_pwm(struct pwm_chip *chip) | ||
| 38 | { | ||
| 39 | return container_of(chip, struct bcm2835_pwm, chip); | ||
| 40 | } | ||
| 41 | |||
| 42 | static int bcm2835_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 43 | { | ||
| 44 | struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); | ||
| 45 | u32 value; | ||
| 46 | |||
| 47 | value = readl(pc->base + PWM_CONTROL); | ||
| 48 | value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm)); | ||
| 49 | value |= (PWM_MODE << PWM_CONTROL_SHIFT(pwm->hwpwm)); | ||
| 50 | writel(value, pc->base + PWM_CONTROL); | ||
| 51 | |||
| 52 | return 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | static void bcm2835_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 56 | { | ||
| 57 | struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); | ||
| 58 | u32 value; | ||
| 59 | |||
| 60 | value = readl(pc->base + PWM_CONTROL); | ||
| 61 | value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm)); | ||
| 62 | writel(value, pc->base + PWM_CONTROL); | ||
| 63 | } | ||
| 64 | |||
| 65 | static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 66 | int duty_ns, int period_ns) | ||
| 67 | { | ||
| 68 | struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); | ||
| 69 | |||
| 70 | if (period_ns <= MIN_PERIOD) { | ||
| 71 | dev_err(pc->dev, "period %d not supported, minimum %d\n", | ||
| 72 | period_ns, MIN_PERIOD); | ||
| 73 | return -EINVAL; | ||
| 74 | } | ||
| 75 | |||
| 76 | writel(duty_ns / pc->scaler, pc->base + DUTY(pwm->hwpwm)); | ||
| 77 | writel(period_ns / pc->scaler, pc->base + PERIOD(pwm->hwpwm)); | ||
| 78 | |||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | static int bcm2835_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 83 | { | ||
| 84 | struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); | ||
| 85 | u32 value; | ||
| 86 | |||
| 87 | value = readl(pc->base + PWM_CONTROL); | ||
| 88 | value |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm); | ||
| 89 | writel(value, pc->base + PWM_CONTROL); | ||
| 90 | |||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | static void bcm2835_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 95 | { | ||
| 96 | struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); | ||
| 97 | u32 value; | ||
| 98 | |||
| 99 | value = readl(pc->base + PWM_CONTROL); | ||
| 100 | value &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm)); | ||
| 101 | writel(value, pc->base + PWM_CONTROL); | ||
| 102 | } | ||
| 103 | |||
| 104 | static int bcm2835_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 105 | enum pwm_polarity polarity) | ||
| 106 | { | ||
| 107 | struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); | ||
| 108 | u32 value; | ||
| 109 | |||
| 110 | value = readl(pc->base + PWM_CONTROL); | ||
| 111 | |||
| 112 | if (polarity == PWM_POLARITY_NORMAL) | ||
| 113 | value &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm)); | ||
| 114 | else | ||
| 115 | value |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm); | ||
| 116 | |||
| 117 | writel(value, pc->base + PWM_CONTROL); | ||
| 118 | |||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | static const struct pwm_ops bcm2835_pwm_ops = { | ||
| 123 | .request = bcm2835_pwm_request, | ||
| 124 | .free = bcm2835_pwm_free, | ||
| 125 | .config = bcm2835_pwm_config, | ||
| 126 | .enable = bcm2835_pwm_enable, | ||
| 127 | .disable = bcm2835_pwm_disable, | ||
| 128 | .set_polarity = bcm2835_set_polarity, | ||
| 129 | .owner = THIS_MODULE, | ||
| 130 | }; | ||
| 131 | |||
| 132 | static int bcm2835_pwm_probe(struct platform_device *pdev) | ||
| 133 | { | ||
| 134 | struct bcm2835_pwm *pc; | ||
| 135 | struct resource *res; | ||
| 136 | int ret; | ||
| 137 | |||
| 138 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); | ||
| 139 | if (!pc) | ||
| 140 | return -ENOMEM; | ||
| 141 | |||
| 142 | pc->dev = &pdev->dev; | ||
| 143 | |||
| 144 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 145 | pc->base = devm_ioremap_resource(&pdev->dev, res); | ||
| 146 | if (IS_ERR(pc->base)) | ||
| 147 | return PTR_ERR(pc->base); | ||
| 148 | |||
| 149 | pc->clk = devm_clk_get(&pdev->dev, NULL); | ||
| 150 | if (IS_ERR(pc->clk)) { | ||
| 151 | dev_err(&pdev->dev, "clock not found: %ld\n", PTR_ERR(pc->clk)); | ||
| 152 | return PTR_ERR(pc->clk); | ||
| 153 | } | ||
| 154 | |||
| 155 | ret = clk_prepare_enable(pc->clk); | ||
| 156 | if (ret) | ||
| 157 | return ret; | ||
| 158 | |||
| 159 | pc->scaler = NSEC_PER_SEC / clk_get_rate(pc->clk); | ||
| 160 | |||
| 161 | pc->chip.dev = &pdev->dev; | ||
| 162 | pc->chip.ops = &bcm2835_pwm_ops; | ||
| 163 | pc->chip.npwm = 2; | ||
| 164 | |||
| 165 | platform_set_drvdata(pdev, pc); | ||
| 166 | |||
| 167 | ret = pwmchip_add(&pc->chip); | ||
| 168 | if (ret < 0) | ||
| 169 | goto add_fail; | ||
| 170 | |||
| 171 | return 0; | ||
| 172 | |||
| 173 | add_fail: | ||
| 174 | clk_disable_unprepare(pc->clk); | ||
| 175 | return ret; | ||
| 176 | } | ||
| 177 | |||
| 178 | static int bcm2835_pwm_remove(struct platform_device *pdev) | ||
| 179 | { | ||
| 180 | struct bcm2835_pwm *pc = platform_get_drvdata(pdev); | ||
| 181 | |||
| 182 | clk_disable_unprepare(pc->clk); | ||
| 183 | |||
| 184 | return pwmchip_remove(&pc->chip); | ||
| 185 | } | ||
| 186 | |||
| 187 | static const struct of_device_id bcm2835_pwm_of_match[] = { | ||
| 188 | { .compatible = "brcm,bcm2835-pwm", }, | ||
| 189 | { /* sentinel */ } | ||
| 190 | }; | ||
| 191 | MODULE_DEVICE_TABLE(of, bcm2835_pwm_of_match); | ||
| 192 | |||
| 193 | static struct platform_driver bcm2835_pwm_driver = { | ||
| 194 | .driver = { | ||
| 195 | .name = "bcm2835-pwm", | ||
| 196 | .of_match_table = bcm2835_pwm_of_match, | ||
| 197 | }, | ||
| 198 | .probe = bcm2835_pwm_probe, | ||
| 199 | .remove = bcm2835_pwm_remove, | ||
| 200 | }; | ||
| 201 | module_platform_driver(bcm2835_pwm_driver); | ||
| 202 | |||
| 203 | MODULE_AUTHOR("Bart Tanghe <bart.tanghe@thomasmore.be"); | ||
| 204 | MODULE_DESCRIPTION("Broadcom BCM2835 PWM driver"); | ||
| 205 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index 0f2cc7ef7784..f9dfc8b6407a 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/mutex.h> | 17 | #include <linux/mutex.h> |
| 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/pm.h> | ||
| 20 | #include <linux/pwm.h> | 21 | #include <linux/pwm.h> |
| 21 | #include <linux/regmap.h> | 22 | #include <linux/regmap.h> |
| 22 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
| @@ -299,7 +300,7 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) | |||
| 299 | { | 300 | { |
| 300 | int ret; | 301 | int ret; |
| 301 | 302 | ||
| 302 | if (fpc->use_count != 0) | 303 | if (fpc->use_count++ != 0) |
| 303 | return 0; | 304 | return 0; |
| 304 | 305 | ||
| 305 | /* select counter clock source */ | 306 | /* select counter clock source */ |
| @@ -316,8 +317,6 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) | |||
| 316 | return ret; | 317 | return ret; |
| 317 | } | 318 | } |
| 318 | 319 | ||
| 319 | fpc->use_count++; | ||
| 320 | |||
| 321 | return 0; | 320 | return 0; |
| 322 | } | 321 | } |
| 323 | 322 | ||
| @@ -399,12 +398,23 @@ static int fsl_pwm_init(struct fsl_pwm_chip *fpc) | |||
| 399 | return 0; | 398 | return 0; |
| 400 | } | 399 | } |
| 401 | 400 | ||
| 401 | static bool fsl_pwm_volatile_reg(struct device *dev, unsigned int reg) | ||
| 402 | { | ||
| 403 | switch (reg) { | ||
| 404 | case FTM_CNT: | ||
| 405 | return true; | ||
| 406 | } | ||
| 407 | return false; | ||
| 408 | } | ||
| 409 | |||
| 402 | static const struct regmap_config fsl_pwm_regmap_config = { | 410 | static const struct regmap_config fsl_pwm_regmap_config = { |
| 403 | .reg_bits = 32, | 411 | .reg_bits = 32, |
| 404 | .reg_stride = 4, | 412 | .reg_stride = 4, |
| 405 | .val_bits = 32, | 413 | .val_bits = 32, |
| 406 | 414 | ||
| 407 | .max_register = FTM_PWMLOAD, | 415 | .max_register = FTM_PWMLOAD, |
| 416 | .volatile_reg = fsl_pwm_volatile_reg, | ||
| 417 | .cache_type = REGCACHE_RBTREE, | ||
| 408 | }; | 418 | }; |
| 409 | 419 | ||
| 410 | static int fsl_pwm_probe(struct platform_device *pdev) | 420 | static int fsl_pwm_probe(struct platform_device *pdev) |
| @@ -427,7 +437,7 @@ static int fsl_pwm_probe(struct platform_device *pdev) | |||
| 427 | if (IS_ERR(base)) | 437 | if (IS_ERR(base)) |
| 428 | return PTR_ERR(base); | 438 | return PTR_ERR(base); |
| 429 | 439 | ||
| 430 | fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, | 440 | fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "ftm_sys", base, |
| 431 | &fsl_pwm_regmap_config); | 441 | &fsl_pwm_regmap_config); |
| 432 | if (IS_ERR(fpc->regmap)) { | 442 | if (IS_ERR(fpc->regmap)) { |
| 433 | dev_err(&pdev->dev, "regmap init failed\n"); | 443 | dev_err(&pdev->dev, "regmap init failed\n"); |
| @@ -478,6 +488,51 @@ static int fsl_pwm_remove(struct platform_device *pdev) | |||
| 478 | return pwmchip_remove(&fpc->chip); | 488 | return pwmchip_remove(&fpc->chip); |
| 479 | } | 489 | } |
| 480 | 490 | ||
| 491 | #ifdef CONFIG_PM_SLEEP | ||
| 492 | static int fsl_pwm_suspend(struct device *dev) | ||
| 493 | { | ||
| 494 | struct fsl_pwm_chip *fpc = dev_get_drvdata(dev); | ||
| 495 | u32 val; | ||
| 496 | |||
| 497 | regcache_cache_only(fpc->regmap, true); | ||
| 498 | regcache_mark_dirty(fpc->regmap); | ||
| 499 | |||
| 500 | /* read from cache */ | ||
| 501 | regmap_read(fpc->regmap, FTM_OUTMASK, &val); | ||
| 502 | if ((val & 0xFF) != 0xFF) { | ||
| 503 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); | ||
| 504 | clk_disable_unprepare(fpc->clk[fpc->cnt_select]); | ||
| 505 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); | ||
| 506 | } | ||
| 507 | |||
| 508 | return 0; | ||
| 509 | } | ||
| 510 | |||
| 511 | static int fsl_pwm_resume(struct device *dev) | ||
| 512 | { | ||
| 513 | struct fsl_pwm_chip *fpc = dev_get_drvdata(dev); | ||
| 514 | u32 val; | ||
| 515 | |||
| 516 | /* read from cache */ | ||
| 517 | regmap_read(fpc->regmap, FTM_OUTMASK, &val); | ||
| 518 | if ((val & 0xFF) != 0xFF) { | ||
| 519 | clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]); | ||
| 520 | clk_prepare_enable(fpc->clk[fpc->cnt_select]); | ||
| 521 | clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); | ||
| 522 | } | ||
| 523 | |||
| 524 | /* restore all registers from cache */ | ||
| 525 | regcache_cache_only(fpc->regmap, false); | ||
| 526 | regcache_sync(fpc->regmap); | ||
| 527 | |||
| 528 | return 0; | ||
| 529 | } | ||
| 530 | #endif | ||
| 531 | |||
| 532 | static const struct dev_pm_ops fsl_pwm_pm_ops = { | ||
| 533 | SET_SYSTEM_SLEEP_PM_OPS(fsl_pwm_suspend, fsl_pwm_resume) | ||
| 534 | }; | ||
| 535 | |||
| 481 | static const struct of_device_id fsl_pwm_dt_ids[] = { | 536 | static const struct of_device_id fsl_pwm_dt_ids[] = { |
| 482 | { .compatible = "fsl,vf610-ftm-pwm", }, | 537 | { .compatible = "fsl,vf610-ftm-pwm", }, |
| 483 | { /* sentinel */ } | 538 | { /* sentinel */ } |
| @@ -488,6 +543,7 @@ static struct platform_driver fsl_pwm_driver = { | |||
| 488 | .driver = { | 543 | .driver = { |
| 489 | .name = "fsl-ftm-pwm", | 544 | .name = "fsl-ftm-pwm", |
| 490 | .of_match_table = fsl_pwm_dt_ids, | 545 | .of_match_table = fsl_pwm_dt_ids, |
| 546 | .pm = &fsl_pwm_pm_ops, | ||
| 491 | }, | 547 | }, |
| 492 | .probe = fsl_pwm_probe, | 548 | .probe = fsl_pwm_probe, |
| 493 | .remove = fsl_pwm_remove, | 549 | .remove = fsl_pwm_remove, |
