diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-19 08:19:07 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-19 08:19:07 -0800 |
commit | 74779e22261172ea728b989310f6ecc991b57d62 (patch) | |
tree | a6c71e02bf6cefc9e0d261dd1ab11f4a7433ed92 | |
parent | 5031a2a7c12b837a0913c4139ebeb6bbff5e1aa5 (diff) | |
parent | 20e8ac3eea4dcfeea6ebeae57cd2c739fa48da11 (diff) |
Merge tag 'for-3.8-rc1' of git://gitorious.org/linux-pwm/linux-pwm
Pull pwm changes from Thierry Reding:
"A new driver has been added for the SPEAr platform and the
TWL4030/6030 driver has been replaced by two drivers that control the
regular PWMs and the PWM driven LEDs provided by the chips.
The vt8500, tiecap, tiehrpwm, i.MX, LPC32xx and Samsung drivers have
all been improved and the device tree bindings now support the PWM
signal polarity."
Fix up trivial conflicts due to __devinit/exit removal.
* tag 'for-3.8-rc1' of git://gitorious.org/linux-pwm/linux-pwm: (21 commits)
pwm: samsung: add missing s3c->pwm_id assignment
pwm: lpc32xx: Set the chip base for dynamic allocation
pwm: lpc32xx: Properly disable the clock on device removal
pwm: lpc32xx: Fix the PWM polarity
pwm: i.MX: eliminate build warning
pwm: Export of_pwm_xlate_with_flags()
pwm: Remove pwm-twl6030 driver
pwm: New driver to support PWM driven LEDs on TWL4030/6030 series of PMICs
pwm: New driver to support PWMs on TWL4030/6030 series of PMICs
pwm: pwm-tiehrpwm: pinctrl support
pwm: tiehrpwm: Add device-tree binding
pwm: pwm-tiehrpwm: Adding TBCLK gating support.
pwm: pwm-tiecap: pinctrl support
pwm: tiecap: Add device-tree binding
pwm: Add TI PWM subsystem driver
pwm: Device tree support for PWM polarity
pwm: vt8500: Ensure PWM clock is enabled during pwm_config
pwm: vt8500: Fix build error
pwm: spear: Staticize spear_pwm_config()
pwm: Add SPEAr PWM chip driver support
...
24 files changed, 1593 insertions, 221 deletions
diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt new file mode 100644 index 00000000000..131e8c11d26 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt @@ -0,0 +1,23 @@ +TI SOC ECAP based APWM controller + +Required properties: +- compatible: Must be "ti,am33xx-ecap" +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. +- reg: physical base address and size of the registers map. + +Optional properties: +- ti,hwmods: Name of the hwmod associated to the ECAP: + "ecap<x>", <x> being the 0-based instance number from the HW spec + +Example: + +ecap0: ecap@0 { + compatible = "ti,am33xx-ecap"; + #pwm-cells = <3>; + reg = <0x48300100 0x80>; + ti,hwmods = "ecap0"; +}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt new file mode 100644 index 00000000000..4fc7079d822 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt @@ -0,0 +1,23 @@ +TI SOC EHRPWM based PWM controller + +Required properties: +- compatible : Must be "ti,am33xx-ehrpwm" +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. +- reg: physical base address and size of the registers map. + +Optional properties: +- ti,hwmods: Name of the hwmod associated to the EHRPWM: + "ehrpwm<x>", <x> being the 0-based instance number from the HW spec + +Example: + +ehrpwm0: ehrpwm@0 { + compatible = "ti,am33xx-ehrpwm"; + #pwm-cells = <3>; + reg = <0x48300200 0x100>; + ti,hwmods = "ehrpwm0"; +}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt new file mode 100644 index 00000000000..f7eae77f835 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt @@ -0,0 +1,31 @@ +TI SOC based PWM Subsystem + +Required properties: +- compatible: Must be "ti,am33xx-pwmss"; +- reg: physical base address and size of the registers map. +- address-cells: Specify the number of u32 entries needed in child nodes. + Should set to 1. +- size-cells: specify number of u32 entries needed to specify child nodes size + in reg property. Should set to 1. +- ranges: describes the address mapping of a memory-mapped bus. Should set to + physical address map of child's base address, physical address within + parent's address space and length of the address map. For am33xx, + 3 set of child register maps present, ECAP register space, EQEP + register space, EHRPWM register space. + +Also child nodes should also populated under PWMSS DT node. + +Example: +pwmss0: pwmss@48300000 { + compatible = "ti,am33xx-pwmss"; + reg = <0x48300000 0x10>; + ti,hwmods = "epwmss0"; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + ranges = <0x48300100 0x48300100 0x80 /* ECAP */ + 0x48300180 0x48300180 0x80 /* EQEP */ + 0x48300200 0x48300200 0x80>; /* EHRPWM */ + + /* child nodes go here */ +}; diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt index 73ec962bfe8..06e67247859 100644 --- a/Documentation/devicetree/bindings/pwm/pwm.txt +++ b/Documentation/devicetree/bindings/pwm/pwm.txt @@ -37,10 +37,21 @@ device: pwm-names = "backlight"; }; +Note that in the example above, specifying the "pwm-names" is redundant +because the name "backlight" would be used as fallback anyway. + pwm-specifier typically encodes the chip-relative PWM number and the PWM -period in nanoseconds. Note that in the example above, specifying the -"pwm-names" is redundant because the name "backlight" would be used as -fallback anyway. +period in nanoseconds. + +Optionally, the pwm-specifier can encode a number of flags in a third cell: +- bit 0: PWM signal polarity (0: normal polarity, 1: inverse polarity) + +Example with optional PWM specifier for inverse polarity + + bl: backlight { + pwms = <&pwm 0 5000000 1>; + pwm-names = "backlight"; + }; 2) PWM controller nodes ----------------------- diff --git a/Documentation/devicetree/bindings/pwm/spear-pwm.txt b/Documentation/devicetree/bindings/pwm/spear-pwm.txt new file mode 100644 index 00000000000..3ac779d8338 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/spear-pwm.txt @@ -0,0 +1,18 @@ +== ST SPEAr SoC PWM controller == + +Required properties: +- compatible: should be one of: + - "st,spear320-pwm" + - "st,spear1340-pwm" +- reg: physical base address and length of the controller's registers +- #pwm-cells: number of cells used to specify PWM which is fixed to 2 on + SPEAr. The first cell specifies the per-chip index of the PWM to use and + the second cell is the period in nanoseconds. + +Example: + + pwm: pwm@a8000000 { + compatible ="st,spear320-pwm"; + reg = <0xa8000000 0x1000>; + #pwm-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt new file mode 100644 index 00000000000..2943ee5fce0 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt @@ -0,0 +1,17 @@ +Texas Instruments TWL series PWM drivers + +Supported PWMs: +On TWL4030 series: PWM1 and PWM2 +On TWL6030 series: PWM0 and PWM1 + +Required properties: +- compatible: "ti,twl4030-pwm" or "ti,twl6030-pwm" +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. + +Example: + +twl_pwm: pwm { + compatible = "ti,twl6030-pwm"; + #pwm-cells = <2>; +}; diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt new file mode 100644 index 00000000000..cb64f3acc10 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt @@ -0,0 +1,17 @@ +Texas Instruments TWL series PWM drivers connected to LED terminals + +Supported PWMs: +On TWL4030 series: PWMA and PWMB (connected to LEDA and LEDB terminals) +On TWL6030 series: LED PWM (mainly used as charging indicator LED) + +Required properties: +- compatible: "ti,twl4030-pwmled" or "ti,twl6030-pwmled" +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. + +Example: + +twl_pwmled: pwmled { + compatible = "ti,twl6030-pwmled"; + #pwm-cells = <2>; +}; diff --git a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt new file mode 100644 index 00000000000..bcc63678a9a --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt @@ -0,0 +1,17 @@ +VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller + +Required properties: +- compatible: should be "via,vt8500-pwm" +- reg: physical base address and length of the controller's registers +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. +- clocks: phandle to the PWM source clock + +Example: + +pwm1: pwm@d8220000 { + #pwm-cells = <2>; + compatible = "via,vt8500-pwm"; + reg = <0xd8220000 0x1000>; + clocks = <&clkpwm>; +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index ed81720e7b2..e513cd99817 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -112,6 +112,17 @@ config PWM_SAMSUNG To compile this driver as a module, choose M here: the module will be called pwm-samsung. +config PWM_SPEAR + tristate "STMicroelectronics SPEAr PWM support" + depends on PLAT_SPEAR + depends on OF + help + Generic PWM framework driver for the PWM controller on ST + SPEAr SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-spear. + config PWM_TEGRA tristate "NVIDIA Tegra PWM support" depends on ARCH_TEGRA @@ -125,6 +136,7 @@ config PWM_TEGRA config PWM_TIECAP tristate "ECAP PWM support" depends on SOC_AM33XX + select PWM_TIPWMSS help PWM driver support for the ECAP APWM controller found on AM33XX TI SOC @@ -135,6 +147,7 @@ config PWM_TIECAP config PWM_TIEHRPWM tristate "EHRPWM PWM support" depends on SOC_AM33XX + select PWM_TIPWMSS help PWM driver support for the EHRPWM controller found on AM33XX TI SOC @@ -142,14 +155,32 @@ config PWM_TIEHRPWM To compile this driver as a module, choose M here: the module will be called pwm-tiehrpwm. -config PWM_TWL6030 - tristate "TWL6030 PWM support" +config PWM_TIPWMSS + bool + depends on SOC_AM33XX && (PWM_TIEHRPWM || PWM_TIECAP) + help + PWM Subsystem driver support for AM33xx SOC. + + PWM submodules require PWM config space access from submodule + drivers and require common parent driver support. + +config PWM_TWL + tristate "TWL4030/6030 PWM support" + depends on TWL4030_CORE + help + Generic PWM framework driver for TWL4030/6030. + + To compile this driver as a module, choose M here: the module + will be called pwm-twl. + +config PWM_TWL_LED + tristate "TWL4030/6030 PWM support for LED drivers" depends on TWL4030_CORE help - Generic PWM framework driver for TWL6030. + Generic PWM framework driver for TWL4030/6030 LED terminals. To compile this driver as a module, choose M here: the module - will be called pwm-twl6030. + will be called pwm-twl-led. config PWM_VT8500 tristate "vt8500 pwm support" diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index acfe4821c58..62a2963cfe5 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -8,8 +8,11 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o +obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o -obj-$(CONFIG_PWM_TWL6030) += pwm-twl6030.o +obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o +obj-$(CONFIG_PWM_TWL) += pwm-twl.o +obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index f5acdaa5270..903138b1884 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -32,6 +32,9 @@ #define MAX_PWMS 1024 +/* flags in the third cell of the DT PWM specifier */ +#define PWM_SPEC_POLARITY (1 << 0) + static DEFINE_MUTEX(pwm_lookup_lock); static LIST_HEAD(pwm_lookup_list); static DEFINE_MUTEX(pwm_lock); @@ -129,6 +132,32 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) return 0; } +struct pwm_device * +of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) +{ + struct pwm_device *pwm; + + if (pc->of_pwm_n_cells < 3) + return ERR_PTR(-EINVAL); + + if (args->args[0] >= pc->npwm) + return ERR_PTR(-EINVAL); + + pwm = pwm_request_from_chip(pc, args->args[0], NULL); + if (IS_ERR(pwm)) + return pwm; + + pwm_set_period(pwm, args->args[1]); + + if (args->args[2] & PWM_SPEC_POLARITY) + pwm_set_polarity(pwm, PWM_POLARITY_INVERSED); + else + pwm_set_polarity(pwm, PWM_POLARITY_NORMAL); + + return pwm; +} +EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags); + static struct pwm_device * of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) { diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 8f26e9fcea9..65a86bdeabe 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -235,7 +235,7 @@ static int imx_pwm_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(imx_pwm_dt_ids, &pdev->dev); - struct imx_pwm_data *data; + const struct imx_pwm_data *data; struct imx_chip *imx; struct resource *r; int ret = 0; diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 015a8223562..14106440294 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -49,9 +49,24 @@ static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, c = 0; /* 0 set division by 256 */ period_cycles = c; + /* The duty-cycle value is as follows: + * + * DUTY-CYCLE HIGH LEVEL + * 1 99.9% + * 25 90.0% + * 128 50.0% + * 220 10.0% + * 255 0.1% + * 0 0.0% + * + * In other words, the register value is duty-cycle % 256 with + * duty-cycle in the range 1-256. + */ c = 256 * duty_ns; do_div(c, period_ns); - duty_cycles = c; + if (c > 255) + c = 255; + duty_cycles = 256 - c; writel(PWM_ENABLE | PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles), lpc32xx->base + (pwm->hwpwm << 2)); @@ -106,6 +121,7 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) lpc32xx->chip.dev = &pdev->dev; lpc32xx->chip.ops = &lpc32xx_pwm_ops; lpc32xx->chip.npwm = 2; + lpc32xx->chip.base = -1; ret = pwmchip_add(&lpc32xx->chip); if (ret < 0) { @@ -121,8 +137,11 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) static int lpc32xx_pwm_remove(struct platform_device *pdev) { struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < lpc32xx->chip.npwm; i++) + pwm_disable(&lpc32xx->chip.pwms[i]); - clk_disable(lpc32xx->clk); return pwmchip_remove(&lpc32xx->chip); } diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index e9b15d099c0..5207e6cd864 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -222,6 +222,7 @@ static int s3c_pwm_probe(struct platform_device *pdev) /* calculate base of control bits in TCON */ s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4; + s3c->pwm_id = id; s3c->chip.dev = &pdev->dev; s3c->chip.ops = &s3c_pwm_ops; s3c->chip.base = -1; diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c new file mode 100644 index 00000000000..83b21d9d5cf --- /dev/null +++ b/drivers/pwm/pwm-spear.c @@ -0,0 +1,276 @@ +/* + * ST Microelectronics SPEAr Pulse Width Modulator driver + * + * Copyright (C) 2012 ST Microelectronics + * Shiraz Hashim <shiraz.hashim@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define NUM_PWM 4 + +/* PWM registers and bits definitions */ +#define PWMCR 0x00 /* Control Register */ +#define PWMCR_PWM_ENABLE 0x1 +#define PWMCR_PRESCALE_SHIFT 2 +#define PWMCR_MIN_PRESCALE 0x00 +#define PWMCR_MAX_PRESCALE 0x3FFF + +#define PWMDCR 0x04 /* Duty Cycle Register */ +#define PWMDCR_MIN_DUTY 0x0001 +#define PWMDCR_MAX_DUTY 0xFFFF + +#define PWMPCR 0x08 /* Period Register */ +#define PWMPCR_MIN_PERIOD 0x0001 +#define PWMPCR_MAX_PERIOD 0xFFFF + +/* Following only available on 13xx SoCs */ +#define PWMMCR 0x3C /* Master Control Register */ +#define PWMMCR_PWM_ENABLE 0x1 + +/** + * struct spear_pwm_chip - struct representing pwm chip + * + * @mmio_base: base address of pwm chip + * @clk: pointer to clk structure of pwm chip + * @chip: linux pwm chip representation + * @dev: pointer to device structure of pwm chip + */ +struct spear_pwm_chip { + void __iomem *mmio_base; + struct clk *clk; + struct pwm_chip chip; + struct device *dev; +}; + +static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct spear_pwm_chip, chip); +} + +static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num, + unsigned long offset) +{ + return readl_relaxed(chip->mmio_base + (num << 4) + offset); +} + +static inline void spear_pwm_writel(struct spear_pwm_chip *chip, + unsigned int num, unsigned long offset, + unsigned long val) +{ + writel_relaxed(val, chip->mmio_base + (num << 4) + offset); +} + +static int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + u64 val, div, clk_rate; + unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc; + int ret; + + /* + * Find pv, dc and prescale to suit duty_ns and period_ns. This is done + * according to formulas described below: + * + * period_ns = 10^9 * (PRESCALE + 1) * PV / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + * + * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) + * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) + */ + clk_rate = clk_get_rate(pc->clk); + while (1) { + div = 1000000000; + div *= 1 + prescale; + val = clk_rate * period_ns; + pv = div64_u64(val, div); + val = clk_rate * duty_ns; + dc = div64_u64(val, div); + + /* if duty_ns and period_ns are not achievable then return */ + if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY) + return -EINVAL; + + /* + * if pv and dc have crossed their upper limit, then increase + * prescale and recalculate pv and dc. + */ + if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) { + if (++prescale > PWMCR_MAX_PRESCALE) + return -EINVAL; + continue; + } + break; + } + + /* + * NOTE: the clock to PWM has to be enabled first before writing to the + * registers. + */ + ret = clk_enable(pc->clk); + if (ret) + return ret; + + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, + prescale << PWMCR_PRESCALE_SHIFT); + spear_pwm_writel(pc, pwm->hwpwm, PWMDCR, dc); + spear_pwm_writel(pc, pwm->hwpwm, PWMPCR, pv); + clk_disable(pc->clk); + + return 0; +} + +static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + int rc = 0; + u32 val; + + rc = clk_enable(pc->clk); + if (!rc) + return rc; + + val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); + val |= PWMCR_PWM_ENABLE; + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); + + return 0; +} + +static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + u32 val; + + val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); + val &= ~PWMCR_PWM_ENABLE; + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); + + clk_disable(pc->clk); +} + +static const struct pwm_ops spear_pwm_ops = { + .config = spear_pwm_config, + .enable = spear_pwm_enable, + .disable = spear_pwm_disable, + .owner = THIS_MODULE, +}; + +static int spear_pwm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spear_pwm_chip *pc; + struct resource *r; + int ret; + u32 val; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resources defined\n"); + return -ENODEV; + } + + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); + if (!pc) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!pc->mmio_base) + return -EADDRNOTAVAIL; + + pc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pc->clk)) + return PTR_ERR(pc->clk); + + pc->dev = &pdev->dev; + platform_set_drvdata(pdev, pc); + + pc->chip.dev = &pdev->dev; + pc->chip.ops = &spear_pwm_ops; + pc->chip.base = -1; + pc->chip.npwm = NUM_PWM; + + ret = clk_prepare(pc->clk); + if (!ret) + return ret; + + if (of_device_is_compatible(np, "st,spear1340-pwm")) { + ret = clk_enable(pc->clk); + if (!ret) { + clk_unprepare(pc->clk); + return ret; + } + /* + * Following enables PWM chip, channels would still be + * enabled individually through their control register + */ + val = readl_relaxed(pc->mmio_base + PWMMCR); + val |= PWMMCR_PWM_ENABLE; + writel_relaxed(val, pc->mmio_base + PWMMCR); + + clk_disable(pc->clk); + } + + ret = pwmchip_add(&pc->chip); + if (!ret) { + clk_unprepare(pc->clk); + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + } + + return ret; +} + +static int spear_pwm_remove(struct platform_device *pdev) +{ + struct spear_pwm_chip *pc = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < NUM_PWM; i++) + pwm_disable(&pc->chip.pwms[i]); + + /* clk was prepared in probe, hence unprepare it here */ + clk_unprepare(pc->clk); + return pwmchip_remove(&pc->chip); +} + +static struct of_device_id spear_pwm_of_match[] = { + { .compatible = "st,spear320-pwm" }, + { .compatible = "st,spear1340-pwm" }, + { } +}; + +MODULE_DEVICE_TABLE(of, spear_pwm_of_match); + +static struct platform_driver spear_pwm_driver = { + .driver = { + .name = "spear-pwm", + .of_match_table = spear_pwm_of_match, + }, + .probe = spear_pwm_probe, + .remove = spear_pwm_remove, +}; + +module_platform_driver(spear_pwm_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Shiraz Hashim <shiraz.hashim@st.com>"); +MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.com>"); +MODULE_ALIAS("platform:spear-pwm"); diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 87c091b245c..5cf016dd982 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -25,6 +25,10 @@ #include <linux/clk.h> #include <linux/pm_runtime.h> #include <linux/pwm.h> +#include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> + +#include "pwm-tipwmss.h" /* ECAP registers and bits definitions */ #define CAP1 0x08 @@ -184,12 +188,24 @@ static const struct pwm_ops ecap_pwm_ops = { .owner = THIS_MODULE, }; +static const struct of_device_id ecap_of_match[] = { + { .compatible = "ti,am33xx-ecap" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ecap_of_match); + static int ecap_pwm_probe(struct platform_device *pdev) { int ret; struct resource *r; struct clk *clk; struct ecap_pwm_chip *pc; + u16 status; + struct pinctrl *pinctrl; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + dev_warn(&pdev->dev, "unable to select pin group\n"); pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) { @@ -211,6 +227,8 @@ static int ecap_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &ecap_pwm_ops; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.of_pwm_n_cells = 3; pc->chip.base = -1; pc->chip.npwm = 1; @@ -231,14 +249,40 @@ static int ecap_pwm_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + status = pwmss_submodule_state_change(pdev->dev.parent, + PWMSS_ECAPCLK_EN); + if (!(status & PWMSS_ECAPCLK_EN_ACK)) { + dev_err(&pdev->dev, "PWMSS config space clock enable failed\n"); + ret = -EINVAL; + goto pwmss_clk_failure; + } + + pm_runtime_put_sync(&pdev->dev); + platform_set_drvdata(pdev, pc); return 0; + +pwmss_clk_failure: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pwmchip_remove(&pc->chip); + return ret; } static int ecap_pwm_remove(struct platform_device *pdev) { struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + /* + * Due to hardware misbehaviour, acknowledge of the stop_req + * is missing. Hence checking of the status bit skipped. + */ + pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return pwmchip_remove(&pc->chip); @@ -246,7 +290,9 @@ static int ecap_pwm_remove(struct platform_device *pdev) static struct platform_driver ecap_pwm_driver = { .driver = { - .name = "ecap", + .name = "ecap", + .owner = THIS_MODULE, + .of_match_table = ecap_of_match, }, .probe = ecap_pwm_probe, .remove = ecap_pwm_remove, diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 9ffd389d0c8..72a6dd40c9e 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -25,6 +25,10 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/pm_runtime.h> +#include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> + +#include "pwm-tipwmss.h" /* EHRPWM registers and bits definitions */ @@ -115,6 +119,7 @@ struct ehrpwm_pwm_chip { void __iomem *mmio_base; unsigned long period_cycles[NUM_PWM_CHANNEL]; enum pwm_polarity polarity[NUM_PWM_CHANNEL]; + struct clk *tbclk; }; static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) @@ -335,6 +340,9 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) /* Channels polarity can be configured from action qualifier module */ configure_polarity(pc, pwm->hwpwm); + /* Enable TBCLK before enabling PWM device */ + clk_enable(pc->tbclk); + /* Enable time counter for free_run */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); return 0; @@ -363,6 +371,9 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); + /* Disabling TBCLK on PWM disable */ + clk_disable(pc->tbclk); + /* Stop Time base counter */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); @@ -392,12 +403,24 @@ static const struct pwm_ops ehrpwm_pwm_ops = { .owner = THIS_MODULE, }; +static const struct of_device_id ehrpwm_of_match[] = { + { .compatible = "ti,am33xx-ehrpwm |