2013-09-13 06:02:59

by Xiubo Li

[permalink] [raw]
Subject: [PATCHv4 0/4] Add Freescale FTM PWM driver.

Hello,

This patch series is the Freescale FTM PWM implementation. And there are 8 channels most supported by the FTM PWM. This implementation is only compatible with device tree definition.

This patch series is based on linux-next and has been tested on Vybrid VF610 Tower board using device tree.

Changes in v4:
- Check for the result and return an error for devm_kzalloc().
- Move pinmux setting from the SoC file to the board specific file.
- Revise the written mistake of 'ret |= FTMSC_CLKEXT;' --> 'reg |= FTMSC_CLKEXT;'.


Changes in v3:
- Remove "availabe" field.
- Remove "fsl,pwm-avaliable-chs" property.
- ...

Changes in v2:
- Remove PWM CPWM/EPWM feature and sysfs.
- Remove some redundant code.
- Revise some code for more readable.
- Remove "fsl,pwm-clk-ps", "fsl,pwm-number", "fsl,pwm-channels",etc.
- Add "fsl,pwm-avaliable-chs", "fsl,pwm-counter-clk", etc.
- Support 8 channels default in dtsi file.
- Add counter clock source selection.
- Rename some function name, macro name, etc.
- Use PWM's and OF's existing function interfaces.
- Split clk_unprepare_enable to clk_unprepare and clk_enable,etc.
- ...

Added in v1:
- Add Freescale FTM PWM driver support.
- Add Freescale FTM PWM node for VF610.
- Enable Enables FTM PWM device for Vybrid VF610 TOWER.
- Add device tree bindings for Freescale.





2013-09-13 06:03:18

by Xiubo Li

[permalink] [raw]
Subject: [PATCHv4 3/4] ARM: dts: Enables FTM PWM device for Vybrid VF610 TOWER board.

Selecting system clock as the counter source clock by default.

Signed-off-by: Xiubo Li <[email protected]>
---
arch/arm/boot/dts/vf610-twr.dts | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/arch/arm/boot/dts/vf610-twr.dts b/arch/arm/boot/dts/vf610-twr.dts
index 1a58678..27cbe91 100644
--- a/arch/arm/boot/dts/vf610-twr.dts
+++ b/arch/arm/boot/dts/vf610-twr.dts
@@ -57,6 +57,31 @@
status = "okay";
};

+&pwm0 {
+ fsl,pwm-counter-clk = "ftm0";
+ pinctrl-names = "ch0-active", "ch0-idle", "ch1-active", "ch1-idle",
+ "ch2-active", "ch2-idle", "ch3-active", "ch3-idle",
+ "ch4-active", "ch4-idle", "ch5-active", "ch5-idle",
+ "ch6-active", "ch6-idle", "ch7-active", "ch7-idle";
+ pinctrl-0 = <&pinctrl_pwm0_ch0_active>;
+ pinctrl-1 = <&pinctrl_pwm0_ch0_idle>;
+ pinctrl-2 = <&pinctrl_pwm0_ch1_active>;
+ pinctrl-3 = <&pinctrl_pwm0_ch1_idle>;
+ pinctrl-4 = <&pinctrl_pwm0_ch2_active>;
+ pinctrl-5 = <&pinctrl_pwm0_ch2_idle>;
+ pinctrl-6 = <&pinctrl_pwm0_ch3_active>;
+ pinctrl-7 = <&pinctrl_pwm0_ch3_idle>;
+ pinctrl-8 = <&pinctrl_pwm0_ch4_active>;
+ pinctrl-9 = <&pinctrl_pwm0_ch4_idle>;
+ pinctrl-10 = <&pinctrl_pwm0_ch5_active>;
+ pinctrl-11 = <&pinctrl_pwm0_ch5_idle>;
+ pinctrl-12 = <&pinctrl_pwm0_ch6_active>;
+ pinctrl-13 = <&pinctrl_pwm0_ch6_idle>;
+ pinctrl-14 = <&pinctrl_pwm0_ch7_active>;
+ pinctrl-15 = <&pinctrl_pwm0_ch7_idle>;
+ status = "okay";
+};
+
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1_1>;
--
1.8.0

2013-09-13 06:03:53

by Xiubo Li

[permalink] [raw]
Subject: [PATCHv4 2/4] ARM: dts: Add Freescale FTM PWM node for VF610.

This adds devicetree node for VF610, and there are 8 channels supported
by default.

Signed-off-by: Xiubo Li <[email protected]>
---
arch/arm/boot/dts/vf610.dtsi | 83 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 82 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/vf610.dtsi b/arch/arm/boot/dts/vf610.dtsi
index 67d929c..9b200ef 100644
--- a/arch/arm/boot/dts/vf610.dtsi
+++ b/arch/arm/boot/dts/vf610.dtsi
@@ -140,6 +140,17 @@
clock-names = "pit";
};

+ pwm0: pwm@40038000 {
+ compatible = "fsl,vf610-ftm-pwm";
+ #pwm-cells = <3>;
+ reg = <0x40038000 0x1000>;
+ clock-names = "ftm0", "ftm0_fix_sel", "ftm0_ext_sel";
+ clocks = <&clks VF610_CLK_FTM0>,
+ <&clks VF610_CLK_FTM0_FIX_SEL>,
+ <&clks VF610_CLK_FTM0_EXT_SEL>;
+ status = "disabled";
+ };
+
wdog@4003e000 {
compatible = "fsl,vf610-wdt", "fsl,imx21-wdt";
reg = <0x4003e000 0x1000>;
@@ -270,16 +281,86 @@
};

pwm0 {
- pinctrl_pwm0_1: pwm0grp_1 {
+ pinctrl_pwm0_ch0_active: pwm0grp_ch0_active {
fsl,pins = <
VF610_PAD_PTB0__FTM0_CH0 0x1582
+ >;
+ };
+ pinctrl_pwm0_ch0_idle: pwm0grp_ch0_idle {
+ fsl,pins = <
+ VF610_PAD_PTB0__FTM0_CH0 0x0000
+ >;
+ };
+ pinctrl_pwm0_ch1_active: pwm0grp_ch1_active {
+ fsl,pins = <
VF610_PAD_PTB1__FTM0_CH1 0x1582
+ >;
+ };
+ pinctrl_pwm0_ch1_idle: pwm0grp_ch1_idle {
+ fsl,pins = <
+ VF610_PAD_PTB1__FTM0_CH1 0x0000
+ >;
+ };
+ pinctrl_pwm0_ch2_active: pwm0grp_ch2_active {
+ fsl,pins = <
VF610_PAD_PTB2__FTM0_CH2 0x1582
+ >;
+ };
+ pinctrl_pwm0_ch2_idle: pwm0grp_ch2_idle {
+ fsl,pins = <
+ VF610_PAD_PTB2__FTM0_CH2 0x0000
+ >;
+ };
+ pinctrl_pwm0_ch3_active: pwm0grp_ch3_active {
+ fsl,pins = <
VF610_PAD_PTB3__FTM0_CH3 0x1582
+ >;
+ };
+ pinctrl_pwm0_ch3_idle: pwm0grp_ch3_idle {
+ fsl,pins = <
+ VF610_PAD_PTB3__FTM0_CH3 0x0000
+ >;
+ };
+ pinctrl_pwm0_ch4_active: pwm0grp_ch4_active {
+ fsl,pins = <
+ VF610_PAD_PTB4__FTM0_CH4 0x1582
+ >;
+ };
+ pinctrl_pwm0_ch4_idle: pwm0grp_ch4_idle {
+ fsl,pins = <
+ VF610_PAD_PTB4__FTM0_CH4 0x0000
+ >;
+ };
+ pinctrl_pwm0_ch5_active: pwm0grp_ch5_active {
+ fsl,pins = <
+ VF610_PAD_PTB5__FTM0_CH5 0x1582
+ >;
+ };
+ pinctrl_pwm0_ch5_idle: pwm0grp_ch5_idle {
+ fsl,pins = <
+ VF610_PAD_PTB5__FTM0_CH5 0x0000
+ >;
+ };
+ pinctrl_pwm0_ch6_active: pwm0grp_ch6_active {
+ fsl,pins = <
VF610_PAD_PTB6__FTM0_CH6 0x1582
+ >;
+ };
+ pinctrl_pwm0_ch6_idle: pwm0grp_ch6_idle {
+ fsl,pins = <
+ VF610_PAD_PTB6__FTM0_CH6 0x0000
+ >;
+ };
+ pinctrl_pwm0_ch7_active: pwm0grp_ch7_active {
+ fsl,pins = <
VF610_PAD_PTB7__FTM0_CH7 0x1582
>;
};
+ pinctrl_pwm0_ch7_idle: pwm0grp_ch7_idle {
+ fsl,pins = <
+ VF610_PAD_PTB7__FTM0_CH7 0x0000
+ >;
+ };
};

qspi0 {
--
1.8.0

2013-09-13 06:03:09

by Xiubo Li

[permalink] [raw]
Subject: [PATCHv4 1/4] pwm: Add Freescale FTM PWM driver support

The FTM PWM device can be found on Vybrid VF610 Tower and Layerscape LS-1 SoCs.

Signed-off-by: Xiubo Li <[email protected]>
Signed-off-by: Jingchang Lu <[email protected]>
Reviewed-by: Sascha Hauer <[email protected]>
---
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-fsl-ftm.c | 507 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 518 insertions(+)
create mode 100644 drivers/pwm/pwm-fsl-ftm.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 75840b5..8144fb0 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -62,6 +62,16 @@ config PWM_BFIN
To compile this driver as a module, choose M here: the module
will be called pwm-bfin.

+config PWM_FSL_FTM
+ tristate "Freescale FTM PWM support"
+ depends on OF
+ help
+ Generic FTM PWM framework driver for Freescale VF610 and
+ Layerscape LS-1 SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-fsl-ftm.
+
config PWM_IMX
tristate "i.MX PWM support"
depends on ARCH_MXC
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 77a8c18..f383784 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_PWM_SYSFS) += sysfs.o
obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
+obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c
new file mode 100644
index 0000000..d67a86d
--- /dev/null
+++ b/drivers/pwm/pwm-fsl-ftm.c
@@ -0,0 +1,507 @@
+/*
+ * Freescale FTM PWM Driver
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/of_address.h>
+#include <linux/pinctrl/consumer.h>
+
+#define FTM_SC 0x00
+#define FTMSC_CLK_MASK 0x03
+#define FTMSC_CLK_OFFSET 0x03
+#define FTMSC_CLKSYS (0x01 << 3)
+#define FTMSC_CLKFIX (0x02 << 3)
+#define FTMSC_CLKEXT (0x03 << 3)
+#define FTMSC_PS_MASK 0x07
+#define FTMSC_PS_OFFSET 0x00
+
+#define FTM_CNT 0x04
+#define FTM_MOD 0x08
+
+#define FTM_CSC_BASE 0x0C
+#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8))
+#define FTMCnSC_MSB BIT(5)
+#define FTMCnSC_MSA BIT(4)
+#define FTMCnSC_ELSB BIT(3)
+#define FTMCnSC_ELSA BIT(2)
+
+#define FTM_CV_BASE 0x10
+#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8))
+
+#define FTM_CNTIN 0x4C
+#define FTM_STATUS 0x50
+
+#define FTM_MODE 0x54
+#define FTMMODE_FTMEN BIT(0)
+#define FTMMODE_INIT BIT(2)
+#define FTMMODE_PWMSYNC BIT(3)
+
+#define FTM_SYNC 0x58
+#define FTM_OUTINIT 0x5C
+#define FTM_OUTMASK 0x60
+#define FTM_COMBINE 0x64
+#define FTM_DEADTIME 0x68
+#define FTM_EXTTRIG 0x6C
+#define FTM_POL 0x70
+#define FTM_FMS 0x74
+#define FTM_FILTER 0x78
+#define FTM_FLTCTRL 0x7C
+#define FTM_QDCTRL 0x80
+#define FTM_CONF 0x84
+#define FTM_FLTPOL 0x88
+#define FTM_SYNCONF 0x8C
+#define FTM_INVCTRL 0x90
+#define FTM_SWOCTRL 0x94
+#define FTM_PWMLOAD 0x98
+
+#define FTM_CNTIN_VAL 0x00
+#define FTM_MAX_CHANNEL 8
+
+enum {
+ FSL_INVALID = 0,
+ FSL_AVAILABLE,
+};
+
+enum {
+ FSL_COUNTER_CLK_SYS = 0,
+ FSL_COUNTER_CLK_FIX,
+ FSL_COUNTER_CLK_EXT,
+ FSL_COUNTER_CLK_MAX,
+};
+
+struct fsl_pwm_chip {
+ struct pwm_chip chip;
+
+ struct clk *sys_clk;
+ struct clk *counter_clk;
+ unsigned int counter_clk_select;
+ unsigned int counter_clk_enable;
+ unsigned int clk_ps;
+
+ void __iomem *base;
+
+ /* pinctrl handle */
+ struct pinctrl *pinctrl;
+};
+
+static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct fsl_pwm_chip, chip);
+}
+
+static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ int ret;
+ struct fsl_pwm_chip *fpc;
+
+ fpc = to_fsl_chip(chip);
+
+ ret = clk_prepare_enable(fpc->sys_clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc;
+
+ fpc = to_fsl_chip(chip);
+
+ clk_disable_unprepare(fpc->sys_clk);
+}
+
+static unsigned long fsl_rate_to_cycles(struct fsl_pwm_chip *fpc,
+ unsigned long time_ns)
+{
+ unsigned long long c;
+ unsigned long ps = 1 << fpc->clk_ps;
+
+ if (fpc->counter_clk)
+ c = clk_get_rate(fpc->counter_clk);
+ else
+ c = clk_get_rate(fpc->sys_clk);
+
+ c = c * time_ns;
+ do_div(c, 1000000000UL);
+ do_div(c, ps);
+
+ return (unsigned long)c;
+}
+
+static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ unsigned long period_cycles, duty_cycles;
+ unsigned long cntin = FTM_CNTIN_VAL;
+ struct fsl_pwm_chip *fpc;
+
+ fpc = to_fsl_chip(chip);
+
+ if (WARN_ON(!test_bit(PWMF_REQUESTED, &pwm->flags)))
+ return -ESHUTDOWN;
+
+ period_cycles = fsl_rate_to_cycles(fpc, period_ns);
+ if (period_cycles > 0xFFFF) {
+ dev_err(chip->dev, "required PWM period cycles(%lu) overflow "
+ "16-bits counter!\n", period_cycles);
+ return -EINVAL;
+ }
+
+ duty_cycles = fsl_rate_to_cycles(fpc, duty_ns);
+ if (duty_cycles >= 0xFFFF) {
+ dev_err(chip->dev, "required PWM duty cycles(%lu) overflow "
+ "16-bits counter!\n", duty_cycles);
+ return -EINVAL;
+ }
+
+ writel(FTMCnSC_MSB | FTMCnSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm));
+
+ writel(0xF0, fpc->base + FTM_OUTMASK);
+ writel(0x0F, fpc->base + FTM_OUTINIT);
+ writel(FTM_CNTIN_VAL, fpc->base + FTM_CNTIN);
+
+ writel(period_cycles + cntin - 1, fpc->base + FTM_MOD);
+ writel(duty_cycles + cntin, fpc->base + FTM_CV(pwm->hwpwm));
+
+ return 0;
+}
+
+static int fsl_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ unsigned long reg;
+ struct fsl_pwm_chip *fpc;
+
+ fpc = to_fsl_chip(chip);
+
+ reg = readl(fpc->base + FTM_POL);
+ if (polarity == PWM_POLARITY_INVERSED)
+ reg |= BIT(pwm->hwpwm);
+ else
+ reg &= ~BIT(pwm->hwpwm);
+ writel(reg, fpc->base + FTM_POL);
+
+ return 0;
+}
+
+static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
+{
+ int ret;
+ unsigned long reg;
+
+ if (fpc->counter_clk_enable++)
+ return 0;
+
+ ret = clk_prepare_enable(fpc->counter_clk);
+ if (ret)
+ return ret;
+
+ reg = readl(fpc->base + FTM_SC);
+ reg &= ~((FTMSC_CLK_MASK << FTMSC_CLK_OFFSET) |
+ (FTMSC_PS_MASK << FTMSC_PS_OFFSET));
+ /* select counter clock source */
+ switch (fpc->counter_clk_select) {
+ case FSL_COUNTER_CLK_SYS:
+ reg |= FTMSC_CLKSYS;
+ break;
+ case FSL_COUNTER_CLK_FIX:
+ reg |= FTMSC_CLKFIX;
+ break;
+ case FSL_COUNTER_CLK_EXT:
+ reg |= FTMSC_CLKEXT;
+ break;
+ default:
+ break;
+ }
+ reg |= fpc->clk_ps;
+ writel(reg, fpc->base + FTM_SC);
+
+ return 0;
+}
+
+static int fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
+{
+ unsigned long reg;
+
+ if (--fpc->counter_clk_enable)
+ return 0;
+
+ writel(0xFF, fpc->base + FTM_OUTMASK);
+ reg = readl(fpc->base + FTM_SC);
+ reg &= ~(FTMSC_CLK_MASK << FTMSC_CLK_OFFSET);
+ writel(reg, fpc->base + FTM_SC);
+
+ clk_disable_unprepare(fpc->counter_clk);
+
+ return 0;
+}
+
+static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ int ret;
+ struct fsl_pwm_chip *fpc;
+ struct pinctrl_state *pins_state;
+ const char *statename;
+
+ fpc = to_fsl_chip(chip);
+
+ statename = kasprintf(GFP_KERNEL, "ch%d-active", pwm->hwpwm);
+ pins_state = pinctrl_lookup_state(fpc->pinctrl,
+ statename);
+ if (IS_ERR(pins_state)) {
+ ret = PTR_ERR(pins_state);
+ dev_err(chip->dev, "could not get \"%s\" pinstate :%d\n",
+ statename, ret);
+ goto out;
+ }
+
+ /* enable pins to be muxed in and configured */
+ ret = pinctrl_select_state(fpc->pinctrl, pins_state);
+ if (ret) {
+ dev_err(chip->dev, "could not set \"%s\" pinstate :%d\n",
+ statename, ret);
+ goto out;
+ }
+
+ fsl_counter_clock_enable(fpc);
+
+out:
+ kfree(statename);
+ return ret;
+}
+
+static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ int ret;
+ struct fsl_pwm_chip *fpc;
+ struct pinctrl_state *pins_state;
+ const char *statename;
+
+ fpc = to_fsl_chip(chip);
+
+ statename = kasprintf(GFP_KERNEL, "ch%d-idle", pwm->hwpwm);
+ pins_state = pinctrl_lookup_state(fpc->pinctrl,
+ statename);
+ if (IS_ERR(pins_state)) {
+ ret = PTR_ERR(pins_state);
+ dev_err(chip->dev, "could not get \"%s\" pinstate :%d\n",
+ statename, ret);
+ goto out;
+ }
+
+ /* enable pins to be muxed in and configured */
+ ret = pinctrl_select_state(fpc->pinctrl, pins_state);
+ if (ret) {
+ dev_err(chip->dev, "could not set \"%s\" pinstate :%d\n",
+ statename, ret);
+ goto out;
+ }
+
+ fsl_counter_clock_disable(fpc);
+
+out:
+ kfree(statename);
+}
+
+static const struct pwm_ops fsl_pwm_ops = {
+ .request = fsl_pwm_request,
+ .free = fsl_pwm_free,
+ .config = fsl_pwm_config,
+ .set_polarity = fsl_pwm_set_polarity,
+ .enable = fsl_pwm_enable,
+ .disable = fsl_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int fsl_pwm_calculate_ps(struct fsl_pwm_chip *fpc)
+{
+ unsigned long long sys_rate, counter_rate, ratio;
+
+ sys_rate = clk_get_rate(fpc->sys_clk);
+ if (!sys_rate)
+ return -EINVAL;
+
+ counter_rate = clk_get_rate(fpc->counter_clk);
+ if (!counter_rate && fpc->counter_clk_select != FSL_COUNTER_CLK_SYS) {
+ dev_warn(fpc->chip.dev,
+ "the counter source clock is a dummy clock, "
+ "so select the system clock as default!\n");
+ }
+
+ if (!counter_rate) {
+ fpc->counter_clk = NULL;
+ fpc->counter_clk_select = FSL_COUNTER_CLK_SYS;
+ fpc->clk_ps = 7;
+ }
+
+ switch (fpc->counter_clk_select) {
+ case FSL_COUNTER_CLK_FIX:
+ ratio = 2 * counter_rate - 1;
+ do_div(ratio, sys_rate);
+ fpc->clk_ps = ratio;
+ break;
+ case FSL_COUNTER_CLK_EXT:
+ ratio = 4 * counter_rate - 1;
+ do_div(ratio, sys_rate);
+ fpc->clk_ps = ratio;
+ break;
+ case FSL_COUNTER_CLK_SYS:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int fsl_pwm_parse_clk_ps(struct fsl_pwm_chip *fpc)
+{
+ const char *cname;
+ int ret, index;
+ struct device_node *np = fpc->chip.dev->of_node;
+
+ ret = of_property_read_string_index(np, "fsl,pwm-counter-clk", 0,
+ &cname);
+ if (ret < 0) {
+ dev_err(fpc->chip.dev,
+ "failed to get \"fsl,pwm-counter-clk\": %d\n",
+ ret);
+ return ret;
+ }
+
+ index = of_property_match_string(np, "clock-names", cname);
+ if (index < 0)
+ return index;
+ if (index >= FSL_COUNTER_CLK_MAX)
+ return -EINVAL;
+ fpc->counter_clk_select = index;
+
+ fpc->sys_clk = devm_clk_get(fpc->chip.dev, "ftm0");
+ if (IS_ERR(fpc->sys_clk)) {
+ ret = PTR_ERR(fpc->sys_clk);
+ dev_err(fpc->chip.dev,
+ "failed to get \"ftm0\" clock %d\n", ret);
+ return ret;
+ }
+
+ switch (fpc->counter_clk_select) {
+ case FSL_COUNTER_CLK_SYS:
+ fpc->counter_clk = NULL;
+ break;
+ case FSL_COUNTER_CLK_FIX:
+ fpc->counter_clk = devm_clk_get(fpc->chip.dev, "ftm0_fix_sel");
+ if (IS_ERR(fpc->counter_clk)) {
+ ret = PTR_ERR(fpc->counter_clk);
+ dev_err(fpc->chip.dev,
+ "failed to get \"ftm0_fix_sel\" "
+ "clock %d\n", ret);
+ return ret;
+ }
+ break;
+ case FSL_COUNTER_CLK_EXT:
+ fpc->counter_clk = devm_clk_get(fpc->chip.dev, "ftm0_ext_sel");
+ if (IS_ERR(fpc->counter_clk)) {
+ ret = PTR_ERR(fpc->counter_clk);
+ dev_err(fpc->chip.dev,
+ "failed to get \"ftm0_ext_sel\" "
+ "clock %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return fsl_pwm_calculate_ps(fpc);
+}
+
+static int fsl_pwm_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct fsl_pwm_chip *fpc;
+ struct resource *res;
+
+ fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL);
+ if (!fpc)
+ return -ENOMEM;
+
+ fpc->chip.dev = &pdev->dev;
+ fpc->counter_clk_enable = 0;
+
+ ret = fsl_pwm_parse_clk_ps(fpc);
+ if (ret < 0)
+ return ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fpc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fpc->base)) {
+ ret = PTR_ERR(fpc->base);
+ return ret;
+ }
+
+ fpc->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR(fpc->pinctrl)) {
+ ret = PTR_ERR(fpc->pinctrl);
+ return ret;
+ }
+
+ fpc->chip.ops = &fsl_pwm_ops;
+ fpc->chip.of_xlate = of_pwm_xlate_with_flags;
+ fpc->chip.of_pwm_n_cells = 3;
+ fpc->chip.base = -1;
+ fpc->chip.npwm = FTM_MAX_CHANNEL;
+ ret = pwmchip_add(&fpc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add PWM chip %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, fpc);
+
+ return 0;
+}
+
+static int fsl_pwm_remove(struct platform_device *pdev)
+{
+ struct fsl_pwm_chip *fpc;
+
+ fpc = platform_get_drvdata(pdev);
+
+ return pwmchip_remove(&fpc->chip);
+}
+
+static const struct of_device_id fsl_pwm_dt_ids[] = {
+ { .compatible = "fsl,vf610-ftm-pwm", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids);
+
+static struct platform_driver fsl_pwm_driver = {
+ .driver = {
+ .name = "fsl-ftm-pwm",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(fsl_pwm_dt_ids),
+ },
+ .probe = fsl_pwm_probe,
+ .remove = fsl_pwm_remove,
+};
+module_platform_driver(fsl_pwm_driver);
+
+MODULE_DESCRIPTION("Freescale FTM PWM Driver");
+MODULE_AUTHOR("Xiubo Li <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.8.0

2013-09-13 06:03:25

by Xiubo Li

[permalink] [raw]
Subject: [PATCHv4 4/4] Documentation: Add device tree bindings for Freescale FTM PWM.

This adds the Document for Freescale FTM PWM driver under
Documentation/devicetree/bindings/pwm/.

Signed-off-by: Xiubo Li <[email protected]>
---
.../devicetree/bindings/pwm/pwm-fsl-ftm.txt | 36 ++++++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt

diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
new file mode 100644
index 0000000..e736806
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
@@ -0,0 +1,36 @@
+Freescale FTM PWM controller
+
+Required properties:
+- compatible: Should be "fsl,vf610-ftm-pwm"
+- reg: Physical base address and length of the controller's registers
+- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
+ the cells format.
+- clock-names : Includes the following module clock source entries:
+ "ftm0" (system clock),
+ "ftm0_fix_sel" (fixed frequency clock),
+ "ftm0_ext_sel" (external clock)
+- clocks : Must contain an entry list for entries in clock-names.
+- fsl,pwm-counter-clk: The FTM PWM counter clock source, should be one of the
+ entries in clock-names.
+- For each channel's pinctrl, the "chN-active" and "chN-idle" states should be
+ implemented at the same time.
+
+Example:
+
+pwm0: pwm@40038000 {
+ compatible = "fsl,vf610-ftm-pwm";
+ reg = <0x40038000 0x1000>;
+ #pwm-cells = <3>;
+ clock-names = "ftm0", "ftm0_fix_sel", "ftm0_ext_sel";
+ clocks = <&clks VF610_CLK_FTM0>,
+ <&clks VF610_CLK_FTM0_FIX_SEL>,
+ <&clks VF610_CLK_FTM0_EXT_SEL>;
+ pinctrl-names = "ch0-active", "ch0-idle", "ch1-active", "ch1-idle",
+ ....;
+ pinctrl-0 = <&pinctrl_pwm0_ch0_active>;
+ pinctrl-1 = <&pinctrl_pwm0_ch0_idle>;
+ pinctrl-2 = <&pinctrl_pwm0_ch1_active>;
+ pinctrl-3 = <&pinctrl_pwm0_ch1_idle>;
+ ...
+ fsl,pwm-counter-clk = "ftm0_ext_sel";
+};
--
1.8.0

2013-09-13 22:34:26

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCHv4 4/4] Documentation: Add device tree bindings for Freescale FTM PWM.

On 09/12/2013 11:58 PM, Xiubo Li wrote:
> This adds the Document for Freescale FTM PWM driver under
> Documentation/devicetree/bindings/pwm/.

> diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt

> +Required properties:

> +- clock-names : Includes the following module clock source entries:
> + "ftm0" (system clock),
> + "ftm0_fix_sel" (fixed frequency clock),
> + "ftm0_ext_sel" (external clock)
> +- clocks : Must contain an entry list for entries in clock-names.

s/an entry list/a clock specifier/
s/for entries/for each entry/

> +- fsl,pwm-counter-clk: The FTM PWM counter clock source, should be one of the
> + entries in clock-names.

So the IP block has 3 input clocks, and also a mux to select which one
to use? That sounds slightly unusual, but possible.

If there is really only 1 clock input to the IP block, and the mux is
part of some other module, then this binding should have only 1 entry in
clocks.

> +- For each channel's pinctrl, the "chN-active" and "chN-idle" states should be
> + implemented at the same time.

I still don't believe that multiple pinctrl states active at once is
something that the pinctrl bindings conceptually support. CC+=LinusW, do
we want to allow this?

Assuming this is allowed, you'd want to write something more like the
following:

pinctrl-names: Must include "chN-active" and "chN-idle" for each channel
ID N in range 0..7.
pinctrl-NNN: One property must exist for each entry in pinctrl-names.
See ../pinctrl/pinctrl-bindings.txt for details of the property values.
> +Example:
> +
> +pwm0: pwm@40038000 {
> + compatible = "fsl,vf610-ftm-pwm";
> + reg = <0x40038000 0x1000>;
> + #pwm-cells = <3>;
> + clock-names = "ftm0", "ftm0_fix_sel", "ftm0_ext_sel";
> + clocks = <&clks VF610_CLK_FTM0>,
> + <&clks VF610_CLK_FTM0_FIX_SEL>,
> + <&clks VF610_CLK_FTM0_EXT_SEL>;
> + pinctrl-names = "ch0-active", "ch0-idle", "ch1-active", "ch1-idle",
> + ....;
> + pinctrl-0 = <&pinctrl_pwm0_ch0_active>;
> + pinctrl-1 = <&pinctrl_pwm0_ch0_idle>;
> + pinctrl-2 = <&pinctrl_pwm0_ch1_active>;
> + pinctrl-3 = <&pinctrl_pwm0_ch1_idle>;
> + ...
> + fsl,pwm-counter-clk = "ftm0_ext_sel";
> +};

2013-09-16 02:49:48

by Xiubo Li-B47053

[permalink] [raw]
Subject: RE: [PATCHv4 4/4] Documentation: Add device tree bindings for Freescale FTM PWM.

> > +- fsl,pwm-counter-clk: The FTM PWM counter clock source, should be
> > +one of the
> > + entries in clock-names.
>
> So the IP block has 3 input clocks, and also a mux to select which one to
> use? That sounds slightly unusual, but possible.
>
> If there is really only 1 clock input to the IP block, and the mux is
> part of some other module, then this binding should have only 1 entry in
> clocks.
>

Yes, there are 3 input clocks that can be selectable, and the mux is inside the FTM IP block.


Thanks.

--
Best Regard,
Xiubo

2013-09-17 10:33:32

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCHv4 4/4] Documentation: Add device tree bindings for Freescale FTM PWM.

On Fri, Sep 13, 2013 at 01:58:42PM +0800, Xiubo Li wrote:
> This adds the Document for Freescale FTM PWM driver under
> Documentation/devicetree/bindings/pwm/.
>
> Signed-off-by: Xiubo Li <[email protected]>
> ---
> .../devicetree/bindings/pwm/pwm-fsl-ftm.txt | 36 ++++++++++++++++++++++
> 1 file changed, 36 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
>
> diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
> new file mode 100644
> index 0000000..e736806
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
> @@ -0,0 +1,36 @@
> +Freescale FTM PWM controller
> +
> +Required properties:
> +- compatible: Should be "fsl,vf610-ftm-pwm"
> +- reg: Physical base address and length of the controller's registers
> +- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
> + the cells format.
> +- clock-names : Includes the following module clock source entries:
> + "ftm0" (system clock),
> + "ftm0_fix_sel" (fixed frequency clock),
> + "ftm0_ext_sel" (external clock)
> +- clocks : Must contain an entry list for entries in clock-names.
> +- fsl,pwm-counter-clk: The FTM PWM counter clock source, should be one of the
> + entries in clock-names.
> +- For each channel's pinctrl, the "chN-active" and "chN-idle" states should be
> + implemented at the same time.

I thought about this active/idle pinctrl stuff again. It should be
removed from the PWM driver. The above is used to control the PWM output
state when the PWM is disabled, so that for example a backlight stays
disabled after a call to pwm_disable.

IMO this is wrong. Instead, the pwm client drivers (pwm_backlight)
shouldn't assume a particular output state of the PWM after a pwm
disable. Instead they should simply set the PWM to a duty cycle of 0%
(or 100% for inverted) to effectively disable it. If then a PWM driver
can optimize this to actually disabling the PWM completely, then fine,
but that's an optimization.

If the handoff of PWM pins from bootloader state to regular state causes
problems like flashing backlights or LEDs due to the pinctrl framework
configuring the iomux before the PWM driver takes over, then the pinctrl
should be done from the client drivers (pwm_backlight, pwm_led), not
from the pwm driver and not from the pinctrl framework before
initializing the clients.

So NACK to this complex pinctrl setup in this pwm driver.

Sascha

--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |