* [PATCH 0/2] PWM driver support for EHRPWM & ECAP
@ 2012-07-13 9:35 ` Philip, Avinash
0 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-13 9:35 UTC (permalink / raw)
To: linux-arm-kernel, linux-omap; +Cc: Philip, Avinash
This patch series adds support for PWM driver support for EHRPWM
and ECAP modules which has been present on AM335x SOC. AM335X SOC has 3
instances of ECAP & EHRPWM.
EHRPWM device can be used to generate PWM waveforms. It has 2 channels of PWM
output and each can be configured for different duty cycle and polarity.
ECAP device can be used as either Capture or APWM mode. In APWM mode, ECAP
can generate PWM waveform.
Patch #1 adds Generic PWM driver support for simple APWM functionality of ECAP
ECAP hardware.
Patch #2 adds PWM driver support for EHRPWM device.
This patch series is based on new PWM framework maintained at [1].
1. https://gitorious.org/linux-pwm/linux-pwm/trees/for-next
2. http://lists.infradead.org/pipermail/linux-arm-kernel/2012-June/104483.html
Philip, Avinash (2):
PWM: ECAP: PWM driver support for ECAP APWM.
PWM: EHRPWM: PWM driver support for EHRPWM.
drivers/pwm/Kconfig | 22 ++
drivers/pwm/Makefile | 2 +
drivers/pwm/pwm-ecap.c | 255 +++++++++++++++++++++++++
drivers/pwm/pwm-ehrpwm.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 755 insertions(+), 0 deletions(-)
create mode 100644 drivers/pwm/pwm-ecap.c
create mode 100644 drivers/pwm/pwm-ehrpwm.c
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 0/2] PWM driver support for EHRPWM & ECAP
@ 2012-07-13 9:35 ` Philip, Avinash
0 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-13 9:35 UTC (permalink / raw)
To: linux-arm-kernel
This patch series adds support for PWM driver support for EHRPWM
and ECAP modules which has been present on AM335x SOC. AM335X SOC has 3
instances of ECAP & EHRPWM.
EHRPWM device can be used to generate PWM waveforms. It has 2 channels of PWM
output and each can be configured for different duty cycle and polarity.
ECAP device can be used as either Capture or APWM mode. In APWM mode, ECAP
can generate PWM waveform.
Patch #1 adds Generic PWM driver support for simple APWM functionality of ECAP
ECAP hardware.
Patch #2 adds PWM driver support for EHRPWM device.
This patch series is based on new PWM framework maintained at [1].
1. https://gitorious.org/linux-pwm/linux-pwm/trees/for-next
2. http://lists.infradead.org/pipermail/linux-arm-kernel/2012-June/104483.html
Philip, Avinash (2):
PWM: ECAP: PWM driver support for ECAP APWM.
PWM: EHRPWM: PWM driver support for EHRPWM.
drivers/pwm/Kconfig | 22 ++
drivers/pwm/Makefile | 2 +
drivers/pwm/pwm-ecap.c | 255 +++++++++++++++++++++++++
drivers/pwm/pwm-ehrpwm.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 755 insertions(+), 0 deletions(-)
create mode 100644 drivers/pwm/pwm-ecap.c
create mode 100644 drivers/pwm/pwm-ehrpwm.c
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-13 9:35 ` Philip, Avinash
@ 2012-07-13 9:35 ` Philip, Avinash
-1 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-13 9:35 UTC (permalink / raw)
To: linux-arm-kernel, linux-omap; +Cc: Philip, Avinash
ECAP hardware on AM33XX SOC supports auxiliary PWM (APWM) feature. This
commit adds PWM driver support for ECAP hardware on AM33XX SOC.
In the ECAP hardware, each PWM pin can also be configured to be in
capture mode. Current implementation only supports PWM mode of
operation. Also, hardware supports sync between multiple PWM pins but
the driver supports simple independent PWM functionality.
Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
---
:100644 100644 94e176e... f20b8f2... M drivers/pwm/Kconfig
:100644 100644 5459702... 7dd90ec... M drivers/pwm/Makefile
:000000 100644 0000000... 81efc9e... A drivers/pwm/pwm-ecap.c
drivers/pwm/Kconfig | 10 ++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-ecap.c | 255 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 266 insertions(+), 0 deletions(-)
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 94e176e..f20b8f2 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -85,4 +85,14 @@ config PWM_VT8500
To compile this driver as a module, choose M here: the module
will be called pwm-vt8500.
+config PWM_ECAP
+ tristate "ECAP PWM support"
+ depends on SOC_AM33XX
+ help
+ PWM driver support for the ECAP APWM controller found on AM33XX
+ TI SOC
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm_ecap.
+
endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 5459702..7dd90ec 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
+obj-$(CONFIG_PWM_ECAP) += pwm-ecap.o
diff --git a/drivers/pwm/pwm-ecap.c b/drivers/pwm/pwm-ecap.c
new file mode 100644
index 0000000..81efc9e
--- /dev/null
+++ b/drivers/pwm/pwm-ecap.c
@@ -0,0 +1,255 @@
+/*
+ * ECAP PWM driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+
+/* ECAP registers and bits definitions */
+#define TSCTR 0x00
+#define CTRPHS 0x04
+#define CAP1 0x08
+#define CAP2 0x0C
+#define CAP3 0x10
+#define CAP4 0x14
+#define ECCTL1 0x28
+#define ECCTL2 0x2A
+#define ECCTL2_APWM_POL_LOW BIT(10)
+#define ECCTL2_APWM_MODE BIT(9)
+#define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6))
+#define ECCTL2_TSCTR_FREERUN BIT(4)
+
+#define ECEINT 0x2C
+#define ECFLG 0x2E
+#define ECCLR 0x30
+#define REVID 0x5c
+
+#define DRIVER_NAME "ecap"
+
+struct ecap_pwm_chip {
+ struct pwm_chip chip;
+ unsigned int clk_rate;
+ void __iomem *mmio_base;
+ int pwm_period_ns;
+ int pwm_duty_ns;
+};
+
+static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct ecap_pwm_chip, chip);
+}
+
+/*
+ * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
+ * duty_ns = 10^9 * duty_cycles / PWM_CLK_RATE
+ */
+static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, duty_cycles;
+ unsigned int reg_val;
+
+ if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
+ return -ERANGE;
+
+ c = pc->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (unsigned long)c;
+
+ if (period_cycles < 1) {
+ period_cycles = 1;
+ duty_cycles = 1;
+ } else {
+ c = pc->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (unsigned long)c;
+ }
+
+ pc->pwm_duty_ns = duty_ns;
+ pc->pwm_period_ns = period_ns;
+
+ pm_runtime_get_sync(pc->chip.dev);
+
+ reg_val = readw(pc->mmio_base + ECCTL2);
+
+ /* Configure PWM mode & disable sync option */
+ reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
+
+ writew(reg_val, pc->mmio_base + ECCTL2);
+
+ if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+ /* Update active registers if not running */
+ writel(duty_cycles, pc->mmio_base + CAP2);
+ writel(period_cycles, pc->mmio_base + CAP1);
+ } else {
+ /*
+ * Update shadow registers to configure period and
+ * compare values. This helps current PWM period to
+ * complete on reconfiguring
+ */
+ writel(duty_cycles, pc->mmio_base + CAP4);
+ writel(period_cycles, pc->mmio_base + CAP3);
+ }
+
+ pm_runtime_put_sync(pc->chip.dev);
+ return 0;
+}
+
+static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ unsigned int reg_val;
+
+ /* Leave clock enabled on enabling PWM */
+ pm_runtime_get_sync(pc->chip.dev);
+
+ /*
+ * Enable 'Free run Time stamp counter mode' to start counter
+ * and 'APWM mode' to enable APWM output
+ */
+ reg_val = readw(pc->mmio_base + ECCTL2);
+ reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
+ writew(reg_val, pc->mmio_base + ECCTL2);
+ return 0;
+}
+
+static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ unsigned int reg_val;
+
+ /*
+ * Disable 'Free run Time stamp counter mode' to stop counter
+ * and 'APWM mode' to put APWM output to low
+ */
+ reg_val = readw(pc->mmio_base + ECCTL2);
+ reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
+ writew(reg_val, pc->mmio_base + ECCTL2);
+
+ /* Disable clock on PWM disable */
+ pm_runtime_put_sync(pc->chip.dev);
+}
+
+static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+ dev_warn(chip->dev, "Removing PWM device without disabling\n");
+ pm_runtime_put_sync(chip->dev);
+ }
+}
+
+static struct pwm_ops ecap_pwm_ops = {
+ .free = ecap_pwm_free,
+ .config = ecap_pwm_config,
+ .enable = ecap_pwm_enable,
+ .disable = ecap_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit ecap_pwm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct clk *clk;
+ struct ecap_pwm_chip *pc;
+
+ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ pc->clk_rate = clk_get_rate(clk);
+ if (!pc->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ pc->chip.dev = &pdev->dev;
+ pc->chip.ops = &ecap_pwm_ops;
+ pc->chip.base = -1;
+ pc->chip.npwm = 1;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!pc->mmio_base) {
+ dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ ret = pwmchip_add(&pc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ platform_set_drvdata(pdev, pc);
+ dev_info(&pdev->dev, "PWM device initialized\n");
+ return 0;
+}
+
+static int __devexit ecap_pwm_remove(struct platform_device *pdev)
+{
+ struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
+ struct pwm_device *pwm;
+
+ if (WARN_ON(!pc))
+ return -ENODEV;
+
+ pwm = &pc->chip.pwms[0];
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pwmchip_remove(&pc->chip);
+ return 0;
+}
+
+static struct platform_driver ecap_pwm_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = ecap_pwm_probe,
+ .remove = __devexit_p(ecap_pwm_remove),
+};
+
+module_platform_driver(ecap_pwm_driver);
+
+MODULE_DESCRIPTION("ECAP PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
--
1.7.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-13 9:35 ` Philip, Avinash
0 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-13 9:35 UTC (permalink / raw)
To: linux-arm-kernel
ECAP hardware on AM33XX SOC supports auxiliary PWM (APWM) feature. This
commit adds PWM driver support for ECAP hardware on AM33XX SOC.
In the ECAP hardware, each PWM pin can also be configured to be in
capture mode. Current implementation only supports PWM mode of
operation. Also, hardware supports sync between multiple PWM pins but
the driver supports simple independent PWM functionality.
Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
---
:100644 100644 94e176e... f20b8f2... M drivers/pwm/Kconfig
:100644 100644 5459702... 7dd90ec... M drivers/pwm/Makefile
:000000 100644 0000000... 81efc9e... A drivers/pwm/pwm-ecap.c
drivers/pwm/Kconfig | 10 ++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-ecap.c | 255 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 266 insertions(+), 0 deletions(-)
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 94e176e..f20b8f2 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -85,4 +85,14 @@ config PWM_VT8500
To compile this driver as a module, choose M here: the module
will be called pwm-vt8500.
+config PWM_ECAP
+ tristate "ECAP PWM support"
+ depends on SOC_AM33XX
+ help
+ PWM driver support for the ECAP APWM controller found on AM33XX
+ TI SOC
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm_ecap.
+
endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 5459702..7dd90ec 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
+obj-$(CONFIG_PWM_ECAP) += pwm-ecap.o
diff --git a/drivers/pwm/pwm-ecap.c b/drivers/pwm/pwm-ecap.c
new file mode 100644
index 0000000..81efc9e
--- /dev/null
+++ b/drivers/pwm/pwm-ecap.c
@@ -0,0 +1,255 @@
+/*
+ * ECAP PWM driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+
+/* ECAP registers and bits definitions */
+#define TSCTR 0x00
+#define CTRPHS 0x04
+#define CAP1 0x08
+#define CAP2 0x0C
+#define CAP3 0x10
+#define CAP4 0x14
+#define ECCTL1 0x28
+#define ECCTL2 0x2A
+#define ECCTL2_APWM_POL_LOW BIT(10)
+#define ECCTL2_APWM_MODE BIT(9)
+#define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6))
+#define ECCTL2_TSCTR_FREERUN BIT(4)
+
+#define ECEINT 0x2C
+#define ECFLG 0x2E
+#define ECCLR 0x30
+#define REVID 0x5c
+
+#define DRIVER_NAME "ecap"
+
+struct ecap_pwm_chip {
+ struct pwm_chip chip;
+ unsigned int clk_rate;
+ void __iomem *mmio_base;
+ int pwm_period_ns;
+ int pwm_duty_ns;
+};
+
+static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct ecap_pwm_chip, chip);
+}
+
+/*
+ * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
+ * duty_ns = 10^9 * duty_cycles / PWM_CLK_RATE
+ */
+static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, duty_cycles;
+ unsigned int reg_val;
+
+ if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
+ return -ERANGE;
+
+ c = pc->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (unsigned long)c;
+
+ if (period_cycles < 1) {
+ period_cycles = 1;
+ duty_cycles = 1;
+ } else {
+ c = pc->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (unsigned long)c;
+ }
+
+ pc->pwm_duty_ns = duty_ns;
+ pc->pwm_period_ns = period_ns;
+
+ pm_runtime_get_sync(pc->chip.dev);
+
+ reg_val = readw(pc->mmio_base + ECCTL2);
+
+ /* Configure PWM mode & disable sync option */
+ reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
+
+ writew(reg_val, pc->mmio_base + ECCTL2);
+
+ if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+ /* Update active registers if not running */
+ writel(duty_cycles, pc->mmio_base + CAP2);
+ writel(period_cycles, pc->mmio_base + CAP1);
+ } else {
+ /*
+ * Update shadow registers to configure period and
+ * compare values. This helps current PWM period to
+ * complete on reconfiguring
+ */
+ writel(duty_cycles, pc->mmio_base + CAP4);
+ writel(period_cycles, pc->mmio_base + CAP3);
+ }
+
+ pm_runtime_put_sync(pc->chip.dev);
+ return 0;
+}
+
+static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ unsigned int reg_val;
+
+ /* Leave clock enabled on enabling PWM */
+ pm_runtime_get_sync(pc->chip.dev);
+
+ /*
+ * Enable 'Free run Time stamp counter mode' to start counter
+ * and 'APWM mode' to enable APWM output
+ */
+ reg_val = readw(pc->mmio_base + ECCTL2);
+ reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
+ writew(reg_val, pc->mmio_base + ECCTL2);
+ return 0;
+}
+
+static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ unsigned int reg_val;
+
+ /*
+ * Disable 'Free run Time stamp counter mode' to stop counter
+ * and 'APWM mode' to put APWM output to low
+ */
+ reg_val = readw(pc->mmio_base + ECCTL2);
+ reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
+ writew(reg_val, pc->mmio_base + ECCTL2);
+
+ /* Disable clock on PWM disable */
+ pm_runtime_put_sync(pc->chip.dev);
+}
+
+static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+ dev_warn(chip->dev, "Removing PWM device without disabling\n");
+ pm_runtime_put_sync(chip->dev);
+ }
+}
+
+static struct pwm_ops ecap_pwm_ops = {
+ .free = ecap_pwm_free,
+ .config = ecap_pwm_config,
+ .enable = ecap_pwm_enable,
+ .disable = ecap_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit ecap_pwm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct clk *clk;
+ struct ecap_pwm_chip *pc;
+
+ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ pc->clk_rate = clk_get_rate(clk);
+ if (!pc->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ pc->chip.dev = &pdev->dev;
+ pc->chip.ops = &ecap_pwm_ops;
+ pc->chip.base = -1;
+ pc->chip.npwm = 1;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!pc->mmio_base) {
+ dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ ret = pwmchip_add(&pc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ platform_set_drvdata(pdev, pc);
+ dev_info(&pdev->dev, "PWM device initialized\n");
+ return 0;
+}
+
+static int __devexit ecap_pwm_remove(struct platform_device *pdev)
+{
+ struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
+ struct pwm_device *pwm;
+
+ if (WARN_ON(!pc))
+ return -ENODEV;
+
+ pwm = &pc->chip.pwms[0];
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pwmchip_remove(&pc->chip);
+ return 0;
+}
+
+static struct platform_driver ecap_pwm_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = ecap_pwm_probe,
+ .remove = __devexit_p(ecap_pwm_remove),
+};
+
+module_platform_driver(ecap_pwm_driver);
+
+MODULE_DESCRIPTION("ECAP PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
--
1.7.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/2] PWM: EHRPWM: PWM driver support for EHRPWM.
2012-07-13 9:35 ` Philip, Avinash
@ 2012-07-13 9:35 ` Philip, Avinash
-1 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-13 9:35 UTC (permalink / raw)
To: linux-arm-kernel, linux-omap; +Cc: Philip, Avinash
Enhanced high resolution PWM module (EHRPWM) hardware can be used to
generate PWM output over 2 channels. This commit adds PWM driver support
for EHRPWM device present on AM33XX SOC. Current implementation supports
simple PWM functionality.
Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
---
:100644 100644 f20b8f2... ad62d7a... M drivers/pwm/Kconfig
:100644 100644 7dd90ec... 636a1d6... M drivers/pwm/Makefile
:000000 100644 0000000... 985d334... A drivers/pwm/pwm-ehrpwm.c
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-ehrpwm.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 487 insertions(+), 0 deletions(-)
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index f20b8f2..ad62d7a 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -95,4 +95,14 @@ config PWM_ECAP
To compile this driver as a module, choose M here: the module
will be called pwm_ecap.
+config PWM_EHRPWM
+ tristate "EHRPWM PWM support"
+ depends on SOC_AM33XX
+ help
+ PWM driver support for the EHRPWM controller found on AM33XX
+ TI SOC
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-ehrpwm.
+
endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 7dd90ec..636a1d6 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
obj-$(CONFIG_PWM_ECAP) += pwm-ecap.o
+obj-$(CONFIG_PWM_EHRPWM) += pwm-ehrpwm.o
diff --git a/drivers/pwm/pwm-ehrpwm.c b/drivers/pwm/pwm-ehrpwm.c
new file mode 100644
index 0000000..985d334
--- /dev/null
+++ b/drivers/pwm/pwm-ehrpwm.c
@@ -0,0 +1,476 @@
+/*
+ * EHRPWM PWM driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+
+/* EHRPWM registers and bits definitions */
+
+/* Time base module registers */
+#define TBCTL 0x00
+#define TBSTS 0x02
+#define TBPHS 0x06
+#define TBCNT 0x08
+#define TBPRD 0x0A
+
+#define TBCTL_RUN_MASK (BIT(15) | BIT(14))
+#define TBCTL_STOP_NEXT 0
+#define TBCTL_STOP_ON_CYCLE BIT(14)
+#define TBCTL_FREE_RUN (BIT(15) | BIT(14))
+#define TBCTL_PRDLD_MASK BIT(3)
+#define TBCTL_PRDLD_SHDW 0
+#define TBCTL_PRDLD_IMDT BIT(3)
+#define TBCTL_CTRMODE_MASK (BIT(1) | BIT(0))
+#define TBCTL_CTRMODE_UP 0
+#define TBCTL_CTRMODE_DOWN BIT(0)
+#define TBCTL_CTRMODE_UPDOWN BIT(1)
+#define TBCTL_CLKDIV_MASK (BIT(12) | BIT(11) | BIT(10) | BIT(9) | \
+ BIT(8) | BIT(7))
+#define TBCTL_CTRMODE_DOWN BIT(0)
+#define TBCTL_CTRMODE_UPDOWN BIT(1)
+#define TBCTL_CTRMODE_FREEZE (BIT(1) | BIT(0))
+#define TBCTL_HSPCLKDIV_SHIFT 7
+#define TBCTL_CLKDIV_SHIFT 10
+
+#define CLKDIV_MAX 7
+#define HSPCLKDIV_MAX 7
+#define PERIOD_MAX 0xFFFF
+
+/* Compare module registers */
+#define CMPCTL 0x0E
+#define CMPA 0x12
+#define CMPB 0x14
+
+/* Action qualifier module registers */
+#define AQCTLA 0x16
+#define AQCTLB 0x18
+#define AQSFRC 0x1A
+#define AQCSFRC 0x1C
+
+#define AQCTL_CBD_MASK (BIT(11) | BIT(10))
+#define AQCTL_CBD_FRCLOW BIT(10)
+#define AQCTL_CBD_FRCHIGH BIT(11)
+#define AQCTL_CBD_FRCTOGGLE (BIT(11) | BIT(10))
+#define AQCTL_CBU_MASK (BIT(9) | BIT(8))
+#define AQCTL_CBU_FRCLOW BIT(8)
+#define AQCTL_CBU_FRCHIGH BIT(9)
+#define AQCTL_CBU_FRCTOGGLE (BIT(9) | BIT(8))
+#define AQCTL_CAD_MASK (BIT(7) | BIT(6))
+#define AQCTL_CAD_FRCLOW BIT(6)
+#define AQCTL_CAD_FRCHIGH BIT(7)
+#define AQCTL_CAD_FRCTOGGLE (BIT(7) | BIT(6))
+#define AQCTL_CAU_MASK (BIT(5) | BIT(4))
+#define AQCTL_CAU_FRCLOW BIT(4)
+#define AQCTL_CAU_FRCHIGH BIT(5)
+#define AQCTL_CAU_FRCTOGGLE (BIT(5) | BIT(4))
+#define AQCTL_PRD_MASK (BIT(3) | BIT(2))
+#define AQCTL_PRD_FRCLOW BIT(2)
+#define AQCTL_PRD_FRCHIGH BIT(3)
+#define AQCTL_PRD_FRCTOGGLE (BIT(3) | BIT(2))
+#define AQCTL_ZRO_MASK (BIT(1) | BIT(0))
+#define AQCTL_ZRO_FRCLOW BIT(0)
+#define AQCTL_ZRO_FRCHIGH BIT(1)
+#define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0))
+
+#define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6))
+#define AQSFRC_RLDCSF_ZRO 0
+#define AQSFRC_RLDCSF_PRD BIT(6)
+#define AQSFRC_RLDCSF_ZROPRD BIT(7)
+#define AQSFRC_RLDCSF_IMDT (BIT(7) | BIT(6))
+#define AQSFRC_OTSFB_MASK BIT(5)
+#define AQSFRC_OTSFB_FRC1 BIT(5)
+#define AQSFRC_ACTSFB_MASK (BIT(3) | BIT(4))
+#define AQSFRC_ACTSFB_NOTH 0
+#define AQSFRC_ACTSFB_CLR BIT(3)
+#define AQSFRC_ACTSFB_SET BIT(4)
+#define AQSFRC_ACTSFB_TGL (BIT(4) | BIT(3))
+#define AQSFRC_OTSFA_MASK BIT(2)
+#define AQSFRC_OTSFA_FRC1 BIT(2)
+#define AQSFRC_ACTSFA_MASK (BIT(1) | BIT(0))
+#define AQSFRC_ACTSFA_NOTH 0
+#define AQSFRC_ACTSFA_CLR BIT(0)
+#define AQSFRC_ACTSFA_SET BIT(1)
+#define AQSFRC_ACTSFA_TGL (BIT(1) | BIT(0))
+
+#define AQCSFRC_CSFB_MASK (BIT(3) | BIT(2))
+#define AQCSFRC_CSFB_FRCDIS 0
+#define AQCSFRC_CSFB_FRCLOW BIT(2)
+#define AQCSFRC_CSFB_FRCHIGH BIT(3)
+#define AQCSFRC_CSFB_DISSWFRC (BIT(3) | BIT(2))
+#define AQCSFRC_CSFA_MASK (BIT(1) | BIT(0))
+#define AQCSFRC_CSFA_FRCDIS 0
+#define AQCSFRC_CSFA_FRCLOW BIT(0)
+#define AQCSFRC_CSFA_FRCHIGH BIT(1)
+#define AQCSFRC_CSFA_DISSWFRC (BIT(1) | BIT(0))
+
+/* Dead band module registers */
+#define DBCTL 0x1E
+#define DBRED 0x20
+#define DBFED 0x22
+
+/* Trip zone module registers */
+#define TZSEL 0x24
+#define TZCTL 0x28
+#define TZEINT 0x2A
+#define TZFLG 0x2C
+#define TZCLR 0x2E
+#define TZFRC 0x30
+
+/* Event trigger module registers */
+#define ETSEL 0x32
+#define ETPS 0x34
+#define ETFLG 0x36
+#define ETCLR 0x38
+#define ETFRC 0x3A
+#define PCCTL 0x3C
+
+/* High resolution module registers */
+#define TBPHSHR 0x04
+#define CMPAHR 0x10
+#define HRCTL 0x40
+
+#define PWM_CHANNEL 2 /* EHRPWM channels */
+
+#define DRIVER_NAME "ehrpwm"
+
+struct ehrpwm_pwm_chip {
+ struct pwm_chip chip;
+ unsigned int clk_rate;
+ void __iomem *mmio_base;
+ int pwm_period_ns;
+ int pwm_duty_ns;
+};
+
+static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct ehrpwm_pwm_chip, chip);
+}
+
+static void ehrpwm_write(void *base, int offset, unsigned long val)
+{
+ writew(val & 0xffff, base + offset);
+}
+
+static void ehrpwm_modify(void *base, int offset, unsigned short mask,
+ unsigned short val)
+{
+ unsigned short regval;
+
+ regval = readw(base + offset);
+ regval &= ~mask;
+ regval |= val & mask;
+ writew(regval, base + offset);
+}
+
+/**
+ * set_prescale_div - Set up the prescaler divider function
+ * @rqst_prescaler: prescaler value min
+ * @prescale_div: prescaler value set
+ * @tb_clk_div: Time Base Control prescaler bits
+ */
+static int set_prescale_div(unsigned long rqst_prescaler,
+ unsigned short *prescale_div, unsigned short *tb_clk_div)
+{
+ unsigned int clkdiv, hspclkdiv;
+
+ for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) {
+ for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) {
+
+ /*
+ * calculations for prescaler value :
+ * prescale_div = HSPCLKDIVIDER * CLKDIVIDER.
+ * HSPCLKDIVIDER = 2 ** hspclkdiv
+ * CLKDIVIDER = (1), if clkdiv == 0 *OR*
+ * (2 * clkdiv), if clkdiv != 0
+ *
+ * Configure prescale_div value such that period
+ * register value is less than 65535.
+ */
+
+ *prescale_div = (1 << clkdiv) *
+ (hspclkdiv ? (hspclkdiv * 2) : 1);
+ if (*prescale_div > rqst_prescaler) {
+ *tb_clk_div = (clkdiv << TBCTL_CLKDIV_SHIFT) |
+ (hspclkdiv << TBCTL_HSPCLKDIV_SHIFT);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+static void configure_chans(struct ehrpwm_pwm_chip *pc, int chan,
+ unsigned long duty_cycles)
+{
+ int cmp_reg, aqctl_reg;
+ unsigned short aqctl_val, aqctl_mask;
+
+ /*
+ * Channels can be configured from action qualifier module.
+ * Channel 0 configured with compare A register and for
+ * up-counter mode.
+ * Channel 1 configured with compare B register and for
+ * up-counter mode.
+ */
+ if (chan == 1) {
+ aqctl_reg = AQCTLB;
+ cmp_reg = CMPB;
+ /* Configure PWM Low from compare B value */
+ aqctl_val = AQCTL_CBU_FRCLOW;
+ aqctl_mask = AQCTL_CBU_MASK;
+ } else {
+ cmp_reg = CMPA;
+ aqctl_reg = AQCTLA;
+ /* Configure PWM Low from compare A value */
+ aqctl_val = AQCTL_CAU_FRCLOW;
+ aqctl_mask = AQCTL_CAU_MASK;
+ }
+
+ /* Configure PWM High from period value and zero value */
+ aqctl_val |= AQCTL_PRD_FRCHIGH | AQCTL_ZRO_FRCHIGH;
+ aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK;
+ ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val);
+
+ ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles);
+}
+
+/*
+ * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE
+ * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE
+ */
+static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, duty_cycles;
+ unsigned short ps_divval, tb_divval, chan;
+
+ if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
+ return -ERANGE;
+
+ c = pc->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (unsigned long)c;
+
+ if (period_cycles < 1) {
+ period_cycles = 1;
+ duty_cycles = 1;
+ } else {
+ c = pc->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (unsigned long)c;
+ }
+
+ pc->pwm_duty_ns = duty_ns;
+ pc->pwm_period_ns = period_ns;
+
+ /* Configure clock prescaler to support Low frequency PWM wave */
+ if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval,
+ &tb_divval)) {
+ dev_err(chip->dev, "Unsupported values\n");
+ return -EINVAL;
+ }
+
+ pm_runtime_get_sync(chip->dev);
+
+ /* Configure shadow loading on Period register */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRDLD_MASK, TBCTL_PRDLD_SHDW);
+
+ /* Update clock prescaler values */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval);
+
+ /* Update period & duty cycle with presacler division */
+ period_cycles = period_cycles / ps_divval;
+ duty_cycles = duty_cycles / ps_divval;
+
+ ehrpwm_write(pc->mmio_base, TBPRD, period_cycles);
+
+ /* Configure ehrpwm counter for up-count mode */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK,
+ TBCTL_CTRMODE_UP);
+
+ /* Configure the channel for duty cycle */
+ chan = pwm->hwpwm;
+ configure_chans(pc, chan, duty_cycles);
+ pm_runtime_put_sync(chip->dev);
+ return 0;
+}
+
+static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ unsigned short aqcsfrc_val, aqcsfrc_mask;
+
+ /* Leave clock enabled on enabling PWM */
+ pm_runtime_get_sync(chip->dev);
+
+ /* Disabling Action Qualifier on PWM output */
+ if (pwm->hwpwm) {
+ aqcsfrc_val = AQCSFRC_CSFB_FRCDIS;
+ aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = AQCSFRC_CSFA_FRCDIS;
+ aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+ }
+
+ /* Changes to shadow mode */
+ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+ AQSFRC_RLDCSF_ZRO);
+
+ ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+ /* Enable time counter for free_run */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN);
+ return 0;
+}
+
+static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ unsigned short aqcsfrc_val, aqcsfrc_mask;
+
+ /* Action Qualifier puts PWM output low forcefully */
+ if (pwm->hwpwm) {
+ aqcsfrc_val = AQCSFRC_CSFB_FRCLOW;
+ aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = AQCSFRC_CSFA_FRCLOW;
+ aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+ }
+
+ /*
+ * Changes to immediate action on Action Qualifier. This puts
+ * Action Qualifier control on PWM output from next TBCLK
+ */
+ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+ AQSFRC_RLDCSF_IMDT);
+
+ ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+ /* Stop Time base counter */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT);
+
+ /* Disable clock on PWM disable */
+ pm_runtime_put_sync(chip->dev);
+}
+
+static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+ dev_warn(chip->dev, "Removing PWM device without disabling\n");
+ pm_runtime_put_sync(chip->dev);
+ }
+}
+
+static struct pwm_ops ehrpwm_pwm_ops = {
+ .free = ehrpwm_pwm_free,
+ .config = ehrpwm_pwm_config,
+ .enable = ehrpwm_pwm_enable,
+ .disable = ehrpwm_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct clk *clk;
+ struct ehrpwm_pwm_chip *pc;
+
+ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ pc->clk_rate = clk_get_rate(clk);
+ if (!pc->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ pc->chip.dev = &pdev->dev;
+ pc->chip.ops = &ehrpwm_pwm_ops;
+ pc->chip.base = -1;
+ pc->chip.npwm = PWM_CHANNEL;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehrpwm_reg");
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!pc->mmio_base) {
+ dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ ret = pwmchip_add(&pc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ platform_set_drvdata(pdev, pc);
+ dev_info(&pdev->dev, "PWM device initialized\n");
+ return 0;
+}
+
+static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev)
+{
+ struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev);
+
+ if (WARN_ON(!pc))
+ return -ENODEV;
+
+ pm_runtime_disable(&pdev->dev);
+ pwmchip_remove(&pc->chip);
+ return 0;
+}
+
+static struct platform_driver ehrpwm_pwm_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = ehrpwm_pwm_probe,
+ .remove = __devexit_p(ehrpwm_pwm_remove),
+};
+
+module_platform_driver(ehrpwm_pwm_driver);
+
+MODULE_DESCRIPTION("EHRPWM PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
--
1.7.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/2] PWM: EHRPWM: PWM driver support for EHRPWM.
@ 2012-07-13 9:35 ` Philip, Avinash
0 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-13 9:35 UTC (permalink / raw)
To: linux-arm-kernel
Enhanced high resolution PWM module (EHRPWM) hardware can be used to
generate PWM output over 2 channels. This commit adds PWM driver support
for EHRPWM device present on AM33XX SOC. Current implementation supports
simple PWM functionality.
Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
---
:100644 100644 f20b8f2... ad62d7a... M drivers/pwm/Kconfig
:100644 100644 7dd90ec... 636a1d6... M drivers/pwm/Makefile
:000000 100644 0000000... 985d334... A drivers/pwm/pwm-ehrpwm.c
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-ehrpwm.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 487 insertions(+), 0 deletions(-)
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index f20b8f2..ad62d7a 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -95,4 +95,14 @@ config PWM_ECAP
To compile this driver as a module, choose M here: the module
will be called pwm_ecap.
+config PWM_EHRPWM
+ tristate "EHRPWM PWM support"
+ depends on SOC_AM33XX
+ help
+ PWM driver support for the EHRPWM controller found on AM33XX
+ TI SOC
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-ehrpwm.
+
endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 7dd90ec..636a1d6 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
obj-$(CONFIG_PWM_ECAP) += pwm-ecap.o
+obj-$(CONFIG_PWM_EHRPWM) += pwm-ehrpwm.o
diff --git a/drivers/pwm/pwm-ehrpwm.c b/drivers/pwm/pwm-ehrpwm.c
new file mode 100644
index 0000000..985d334
--- /dev/null
+++ b/drivers/pwm/pwm-ehrpwm.c
@@ -0,0 +1,476 @@
+/*
+ * EHRPWM PWM driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+
+/* EHRPWM registers and bits definitions */
+
+/* Time base module registers */
+#define TBCTL 0x00
+#define TBSTS 0x02
+#define TBPHS 0x06
+#define TBCNT 0x08
+#define TBPRD 0x0A
+
+#define TBCTL_RUN_MASK (BIT(15) | BIT(14))
+#define TBCTL_STOP_NEXT 0
+#define TBCTL_STOP_ON_CYCLE BIT(14)
+#define TBCTL_FREE_RUN (BIT(15) | BIT(14))
+#define TBCTL_PRDLD_MASK BIT(3)
+#define TBCTL_PRDLD_SHDW 0
+#define TBCTL_PRDLD_IMDT BIT(3)
+#define TBCTL_CTRMODE_MASK (BIT(1) | BIT(0))
+#define TBCTL_CTRMODE_UP 0
+#define TBCTL_CTRMODE_DOWN BIT(0)
+#define TBCTL_CTRMODE_UPDOWN BIT(1)
+#define TBCTL_CLKDIV_MASK (BIT(12) | BIT(11) | BIT(10) | BIT(9) | \
+ BIT(8) | BIT(7))
+#define TBCTL_CTRMODE_DOWN BIT(0)
+#define TBCTL_CTRMODE_UPDOWN BIT(1)
+#define TBCTL_CTRMODE_FREEZE (BIT(1) | BIT(0))
+#define TBCTL_HSPCLKDIV_SHIFT 7
+#define TBCTL_CLKDIV_SHIFT 10
+
+#define CLKDIV_MAX 7
+#define HSPCLKDIV_MAX 7
+#define PERIOD_MAX 0xFFFF
+
+/* Compare module registers */
+#define CMPCTL 0x0E
+#define CMPA 0x12
+#define CMPB 0x14
+
+/* Action qualifier module registers */
+#define AQCTLA 0x16
+#define AQCTLB 0x18
+#define AQSFRC 0x1A
+#define AQCSFRC 0x1C
+
+#define AQCTL_CBD_MASK (BIT(11) | BIT(10))
+#define AQCTL_CBD_FRCLOW BIT(10)
+#define AQCTL_CBD_FRCHIGH BIT(11)
+#define AQCTL_CBD_FRCTOGGLE (BIT(11) | BIT(10))
+#define AQCTL_CBU_MASK (BIT(9) | BIT(8))
+#define AQCTL_CBU_FRCLOW BIT(8)
+#define AQCTL_CBU_FRCHIGH BIT(9)
+#define AQCTL_CBU_FRCTOGGLE (BIT(9) | BIT(8))
+#define AQCTL_CAD_MASK (BIT(7) | BIT(6))
+#define AQCTL_CAD_FRCLOW BIT(6)
+#define AQCTL_CAD_FRCHIGH BIT(7)
+#define AQCTL_CAD_FRCTOGGLE (BIT(7) | BIT(6))
+#define AQCTL_CAU_MASK (BIT(5) | BIT(4))
+#define AQCTL_CAU_FRCLOW BIT(4)
+#define AQCTL_CAU_FRCHIGH BIT(5)
+#define AQCTL_CAU_FRCTOGGLE (BIT(5) | BIT(4))
+#define AQCTL_PRD_MASK (BIT(3) | BIT(2))
+#define AQCTL_PRD_FRCLOW BIT(2)
+#define AQCTL_PRD_FRCHIGH BIT(3)
+#define AQCTL_PRD_FRCTOGGLE (BIT(3) | BIT(2))
+#define AQCTL_ZRO_MASK (BIT(1) | BIT(0))
+#define AQCTL_ZRO_FRCLOW BIT(0)
+#define AQCTL_ZRO_FRCHIGH BIT(1)
+#define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0))
+
+#define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6))
+#define AQSFRC_RLDCSF_ZRO 0
+#define AQSFRC_RLDCSF_PRD BIT(6)
+#define AQSFRC_RLDCSF_ZROPRD BIT(7)
+#define AQSFRC_RLDCSF_IMDT (BIT(7) | BIT(6))
+#define AQSFRC_OTSFB_MASK BIT(5)
+#define AQSFRC_OTSFB_FRC1 BIT(5)
+#define AQSFRC_ACTSFB_MASK (BIT(3) | BIT(4))
+#define AQSFRC_ACTSFB_NOTH 0
+#define AQSFRC_ACTSFB_CLR BIT(3)
+#define AQSFRC_ACTSFB_SET BIT(4)
+#define AQSFRC_ACTSFB_TGL (BIT(4) | BIT(3))
+#define AQSFRC_OTSFA_MASK BIT(2)
+#define AQSFRC_OTSFA_FRC1 BIT(2)
+#define AQSFRC_ACTSFA_MASK (BIT(1) | BIT(0))
+#define AQSFRC_ACTSFA_NOTH 0
+#define AQSFRC_ACTSFA_CLR BIT(0)
+#define AQSFRC_ACTSFA_SET BIT(1)
+#define AQSFRC_ACTSFA_TGL (BIT(1) | BIT(0))
+
+#define AQCSFRC_CSFB_MASK (BIT(3) | BIT(2))
+#define AQCSFRC_CSFB_FRCDIS 0
+#define AQCSFRC_CSFB_FRCLOW BIT(2)
+#define AQCSFRC_CSFB_FRCHIGH BIT(3)
+#define AQCSFRC_CSFB_DISSWFRC (BIT(3) | BIT(2))
+#define AQCSFRC_CSFA_MASK (BIT(1) | BIT(0))
+#define AQCSFRC_CSFA_FRCDIS 0
+#define AQCSFRC_CSFA_FRCLOW BIT(0)
+#define AQCSFRC_CSFA_FRCHIGH BIT(1)
+#define AQCSFRC_CSFA_DISSWFRC (BIT(1) | BIT(0))
+
+/* Dead band module registers */
+#define DBCTL 0x1E
+#define DBRED 0x20
+#define DBFED 0x22
+
+/* Trip zone module registers */
+#define TZSEL 0x24
+#define TZCTL 0x28
+#define TZEINT 0x2A
+#define TZFLG 0x2C
+#define TZCLR 0x2E
+#define TZFRC 0x30
+
+/* Event trigger module registers */
+#define ETSEL 0x32
+#define ETPS 0x34
+#define ETFLG 0x36
+#define ETCLR 0x38
+#define ETFRC 0x3A
+#define PCCTL 0x3C
+
+/* High resolution module registers */
+#define TBPHSHR 0x04
+#define CMPAHR 0x10
+#define HRCTL 0x40
+
+#define PWM_CHANNEL 2 /* EHRPWM channels */
+
+#define DRIVER_NAME "ehrpwm"
+
+struct ehrpwm_pwm_chip {
+ struct pwm_chip chip;
+ unsigned int clk_rate;
+ void __iomem *mmio_base;
+ int pwm_period_ns;
+ int pwm_duty_ns;
+};
+
+static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct ehrpwm_pwm_chip, chip);
+}
+
+static void ehrpwm_write(void *base, int offset, unsigned long val)
+{
+ writew(val & 0xffff, base + offset);
+}
+
+static void ehrpwm_modify(void *base, int offset, unsigned short mask,
+ unsigned short val)
+{
+ unsigned short regval;
+
+ regval = readw(base + offset);
+ regval &= ~mask;
+ regval |= val & mask;
+ writew(regval, base + offset);
+}
+
+/**
+ * set_prescale_div - Set up the prescaler divider function
+ * @rqst_prescaler: prescaler value min
+ * @prescale_div: prescaler value set
+ * @tb_clk_div: Time Base Control prescaler bits
+ */
+static int set_prescale_div(unsigned long rqst_prescaler,
+ unsigned short *prescale_div, unsigned short *tb_clk_div)
+{
+ unsigned int clkdiv, hspclkdiv;
+
+ for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) {
+ for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) {
+
+ /*
+ * calculations for prescaler value :
+ * prescale_div = HSPCLKDIVIDER * CLKDIVIDER.
+ * HSPCLKDIVIDER = 2 ** hspclkdiv
+ * CLKDIVIDER = (1), if clkdiv == 0 *OR*
+ * (2 * clkdiv), if clkdiv != 0
+ *
+ * Configure prescale_div value such that period
+ * register value is less than 65535.
+ */
+
+ *prescale_div = (1 << clkdiv) *
+ (hspclkdiv ? (hspclkdiv * 2) : 1);
+ if (*prescale_div > rqst_prescaler) {
+ *tb_clk_div = (clkdiv << TBCTL_CLKDIV_SHIFT) |
+ (hspclkdiv << TBCTL_HSPCLKDIV_SHIFT);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+static void configure_chans(struct ehrpwm_pwm_chip *pc, int chan,
+ unsigned long duty_cycles)
+{
+ int cmp_reg, aqctl_reg;
+ unsigned short aqctl_val, aqctl_mask;
+
+ /*
+ * Channels can be configured from action qualifier module.
+ * Channel 0 configured with compare A register and for
+ * up-counter mode.
+ * Channel 1 configured with compare B register and for
+ * up-counter mode.
+ */
+ if (chan == 1) {
+ aqctl_reg = AQCTLB;
+ cmp_reg = CMPB;
+ /* Configure PWM Low from compare B value */
+ aqctl_val = AQCTL_CBU_FRCLOW;
+ aqctl_mask = AQCTL_CBU_MASK;
+ } else {
+ cmp_reg = CMPA;
+ aqctl_reg = AQCTLA;
+ /* Configure PWM Low from compare A value */
+ aqctl_val = AQCTL_CAU_FRCLOW;
+ aqctl_mask = AQCTL_CAU_MASK;
+ }
+
+ /* Configure PWM High from period value and zero value */
+ aqctl_val |= AQCTL_PRD_FRCHIGH | AQCTL_ZRO_FRCHIGH;
+ aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK;
+ ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val);
+
+ ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles);
+}
+
+/*
+ * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE
+ * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE
+ */
+static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, duty_cycles;
+ unsigned short ps_divval, tb_divval, chan;
+
+ if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
+ return -ERANGE;
+
+ c = pc->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (unsigned long)c;
+
+ if (period_cycles < 1) {
+ period_cycles = 1;
+ duty_cycles = 1;
+ } else {
+ c = pc->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (unsigned long)c;
+ }
+
+ pc->pwm_duty_ns = duty_ns;
+ pc->pwm_period_ns = period_ns;
+
+ /* Configure clock prescaler to support Low frequency PWM wave */
+ if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval,
+ &tb_divval)) {
+ dev_err(chip->dev, "Unsupported values\n");
+ return -EINVAL;
+ }
+
+ pm_runtime_get_sync(chip->dev);
+
+ /* Configure shadow loading on Period register */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRDLD_MASK, TBCTL_PRDLD_SHDW);
+
+ /* Update clock prescaler values */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval);
+
+ /* Update period & duty cycle with presacler division */
+ period_cycles = period_cycles / ps_divval;
+ duty_cycles = duty_cycles / ps_divval;
+
+ ehrpwm_write(pc->mmio_base, TBPRD, period_cycles);
+
+ /* Configure ehrpwm counter for up-count mode */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK,
+ TBCTL_CTRMODE_UP);
+
+ /* Configure the channel for duty cycle */
+ chan = pwm->hwpwm;
+ configure_chans(pc, chan, duty_cycles);
+ pm_runtime_put_sync(chip->dev);
+ return 0;
+}
+
+static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ unsigned short aqcsfrc_val, aqcsfrc_mask;
+
+ /* Leave clock enabled on enabling PWM */
+ pm_runtime_get_sync(chip->dev);
+
+ /* Disabling Action Qualifier on PWM output */
+ if (pwm->hwpwm) {
+ aqcsfrc_val = AQCSFRC_CSFB_FRCDIS;
+ aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = AQCSFRC_CSFA_FRCDIS;
+ aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+ }
+
+ /* Changes to shadow mode */
+ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+ AQSFRC_RLDCSF_ZRO);
+
+ ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+ /* Enable time counter for free_run */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN);
+ return 0;
+}
+
+static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ unsigned short aqcsfrc_val, aqcsfrc_mask;
+
+ /* Action Qualifier puts PWM output low forcefully */
+ if (pwm->hwpwm) {
+ aqcsfrc_val = AQCSFRC_CSFB_FRCLOW;
+ aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = AQCSFRC_CSFA_FRCLOW;
+ aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+ }
+
+ /*
+ * Changes to immediate action on Action Qualifier. This puts
+ * Action Qualifier control on PWM output from next TBCLK
+ */
+ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+ AQSFRC_RLDCSF_IMDT);
+
+ ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+ /* Stop Time base counter */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT);
+
+ /* Disable clock on PWM disable */
+ pm_runtime_put_sync(chip->dev);
+}
+
+static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+ dev_warn(chip->dev, "Removing PWM device without disabling\n");
+ pm_runtime_put_sync(chip->dev);
+ }
+}
+
+static struct pwm_ops ehrpwm_pwm_ops = {
+ .free = ehrpwm_pwm_free,
+ .config = ehrpwm_pwm_config,
+ .enable = ehrpwm_pwm_enable,
+ .disable = ehrpwm_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct clk *clk;
+ struct ehrpwm_pwm_chip *pc;
+
+ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ pc->clk_rate = clk_get_rate(clk);
+ if (!pc->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ pc->chip.dev = &pdev->dev;
+ pc->chip.ops = &ehrpwm_pwm_ops;
+ pc->chip.base = -1;
+ pc->chip.npwm = PWM_CHANNEL;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehrpwm_reg");
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!pc->mmio_base) {
+ dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ ret = pwmchip_add(&pc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ platform_set_drvdata(pdev, pc);
+ dev_info(&pdev->dev, "PWM device initialized\n");
+ return 0;
+}
+
+static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev)
+{
+ struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev);
+
+ if (WARN_ON(!pc))
+ return -ENODEV;
+
+ pm_runtime_disable(&pdev->dev);
+ pwmchip_remove(&pc->chip);
+ return 0;
+}
+
+static struct platform_driver ehrpwm_pwm_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = ehrpwm_pwm_probe,
+ .remove = __devexit_p(ehrpwm_pwm_remove),
+};
+
+module_platform_driver(ehrpwm_pwm_driver);
+
+MODULE_DESCRIPTION("EHRPWM PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
--
1.7.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-13 9:35 ` Philip, Avinash
@ 2012-07-23 6:52 ` Thierry Reding
-1 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-23 6:52 UTC (permalink / raw)
To: Philip, Avinash; +Cc: linux-arm-kernel, linux-omap
[-- Attachment #1: Type: text/plain, Size: 11872 bytes --]
On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> ECAP hardware on AM33XX SOC supports auxiliary PWM (APWM) feature. This
> commit adds PWM driver support for ECAP hardware on AM33XX SOC.
>
> In the ECAP hardware, each PWM pin can also be configured to be in
> capture mode. Current implementation only supports PWM mode of
> operation. Also, hardware supports sync between multiple PWM pins but
> the driver supports simple independent PWM functionality.
>
> Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
> Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
> ---
> :100644 100644 94e176e... f20b8f2... M drivers/pwm/Kconfig
> :100644 100644 5459702... 7dd90ec... M drivers/pwm/Makefile
> :000000 100644 0000000... 81efc9e... A drivers/pwm/pwm-ecap.c
> drivers/pwm/Kconfig | 10 ++
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-ecap.c | 255 ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 266 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 94e176e..f20b8f2 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -85,4 +85,14 @@ config PWM_VT8500
> To compile this driver as a module, choose M here: the module
> will be called pwm-vt8500.
>
> +config PWM_ECAP
> + tristate "ECAP PWM support"
> + depends on SOC_AM33XX
> + help
> + PWM driver support for the ECAP APWM controller found on AM33XX
> + TI SOC
> +
> + To compile this driver as a module, choose M here: the module
> + will be called pwm_ecap.
> +
> endif
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 5459702..7dd90ec 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -7,3 +7,4 @@ obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
> obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
> obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
> obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
> +obj-$(CONFIG_PWM_ECAP) += pwm-ecap.o
Both the Kconfig and Makefile should have the entries ordered
alphabetically.
> diff --git a/drivers/pwm/pwm-ecap.c b/drivers/pwm/pwm-ecap.c
> new file mode 100644
> index 0000000..81efc9e
> --- /dev/null
> +++ b/drivers/pwm/pwm-ecap.c
> @@ -0,0 +1,255 @@
> +/*
> + * ECAP PWM driver
> + *
> + * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/pwm.h>
> +
> +/* ECAP registers and bits definitions */
> +#define TSCTR 0x00
> +#define CTRPHS 0x04
> +#define CAP1 0x08
> +#define CAP2 0x0C
> +#define CAP3 0x10
> +#define CAP4 0x14
> +#define ECCTL1 0x28
These registers are not used. I guess there is some use to list all
registers here but on the other hand the majority are unused so they
just clutter the driver.
> +#define ECCTL2_APWM_POL_LOW BIT(10)
This bit is never used.
> +#define ECEINT 0x2C
> +#define ECFLG 0x2E
> +#define ECCLR 0x30
> +#define REVID 0x5c
These are unused as well.
> +
> +#define DRIVER_NAME "ecap"
You only use this once when defining the struct platform_driver, so
maybe you can just drop it.
> +
> +struct ecap_pwm_chip {
> + struct pwm_chip chip;
> + unsigned int clk_rate;
> + void __iomem *mmio_base;
> + int pwm_period_ns;
> + int pwm_duty_ns;
> +};
The pwm_period_ns and pwm_duty_ns don't seem to be used at all. Can they
be dropped?
> +
> +static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
> +{
> + return container_of(chip, struct ecap_pwm_chip, chip);
> +}
> +
> +/*
> + * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
> + * duty_ns = 10^9 * duty_cycles / PWM_CLK_RATE
> + */
> +static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
> + int duty_ns, int period_ns)
> +{
> + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
> + unsigned long long c;
> + unsigned long period_cycles, duty_cycles;
> + unsigned int reg_val;
> +
> + if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
> + return -ERANGE;
> +
> + c = pc->clk_rate;
> + c = c * period_ns;
> + do_div(c, NSEC_PER_SEC);
> + period_cycles = (unsigned long)c;
> +
> + if (period_cycles < 1) {
> + period_cycles = 1;
> + duty_cycles = 1;
> + } else {
> + c = pc->clk_rate;
> + c = c * duty_ns;
> + do_div(c, NSEC_PER_SEC);
> + duty_cycles = (unsigned long)c;
> + }
> +
> + pc->pwm_duty_ns = duty_ns;
> + pc->pwm_period_ns = period_ns;
> +
> + pm_runtime_get_sync(pc->chip.dev);
> +
> + reg_val = readw(pc->mmio_base + ECCTL2);
> +
> + /* Configure PWM mode & disable sync option */
> + reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
> +
> + writew(reg_val, pc->mmio_base + ECCTL2);
> +
> + if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
> + /* Update active registers if not running */
> + writel(duty_cycles, pc->mmio_base + CAP2);
> + writel(period_cycles, pc->mmio_base + CAP1);
> + } else {
> + /*
> + * Update shadow registers to configure period and
> + * compare values. This helps current PWM period to
> + * complete on reconfiguring
> + */
> + writel(duty_cycles, pc->mmio_base + CAP4);
> + writel(period_cycles, pc->mmio_base + CAP3);
> + }
> +
> + pm_runtime_put_sync(pc->chip.dev);
> + return 0;
> +}
> +
> +static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
> + unsigned int reg_val;
> +
> + /* Leave clock enabled on enabling PWM */
> + pm_runtime_get_sync(pc->chip.dev);
> +
> + /*
> + * Enable 'Free run Time stamp counter mode' to start counter
> + * and 'APWM mode' to enable APWM output
> + */
> + reg_val = readw(pc->mmio_base + ECCTL2);
> + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
You already set the APWM_MODE bit in .config(), why is it needed here
again? Seeing that .disable() clears the bit as well, perhaps leaving it
clear in .config() is the better option.
> + writew(reg_val, pc->mmio_base + ECCTL2);
> + return 0;
> +}
> +
> +static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
> + unsigned int reg_val;
> +
> + /*
> + * Disable 'Free run Time stamp counter mode' to stop counter
> + * and 'APWM mode' to put APWM output to low
> + */
> + reg_val = readw(pc->mmio_base + ECCTL2);
> + reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
> + writew(reg_val, pc->mmio_base + ECCTL2);
> +
> + /* Disable clock on PWM disable */
> + pm_runtime_put_sync(pc->chip.dev);
> +}
> +
> +static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + if (test_bit(PWMF_ENABLED, &pwm->flags)) {
> + dev_warn(chip->dev, "Removing PWM device without disabling\n");
> + pm_runtime_put_sync(chip->dev);
> + }
> +}
I wonder whether we want to have something like this in the PWM core at
some point. Maybe we should call .disable() automatically when they are
freed, or alternatively return -EBUSY if a PWM is freed but still
enabled. I think I prefer the latter. For now we can leave this in,
because I don't want to make any such core changes for 3.6 now that the
merge window is open.
> +
> +static struct pwm_ops ecap_pwm_ops = {
> + .free = ecap_pwm_free,
> + .config = ecap_pwm_config,
> + .enable = ecap_pwm_enable,
> + .disable = ecap_pwm_disable,
> + .owner = THIS_MODULE,
> +};
This should be "static const pwm_ops ...".
> +
> +static int __devinit ecap_pwm_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct resource *r;
> + struct clk *clk;
> + struct ecap_pwm_chip *pc;
> +
> + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
> + if (!pc) {
> + dev_err(&pdev->dev, "failed to allocate memory\n");
> + return -ENOMEM;
> + }
> +
> + clk = devm_clk_get(&pdev->dev, "fck");
> + if (IS_ERR(clk)) {
> + dev_err(&pdev->dev, "failed to get clock\n");
> + return PTR_ERR(clk);
> + }
> +
> + pc->clk_rate = clk_get_rate(clk);
> + if (!pc->clk_rate) {
> + dev_err(&pdev->dev, "failed to get clock rate\n");
> + return -EINVAL;
> + }
> +
> + pc->chip.dev = &pdev->dev;
> + pc->chip.ops = &ecap_pwm_ops;
> + pc->chip.base = -1;
> + pc->chip.npwm = 1;
The cover letter mentions that the AM335x has 3 instances of the ECAP.
Is there any chance that they can be unified in one driver instance
(i.e. .npwm = 3?).
> +
> + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> + if (r == NULL) {
You use "if (!ptr)" everywhere else, maybe you should make this
consistent? Also the platform_get_resource_byname() is really only
useful for devices that have several register regions associated with
them. I don't know your hardware in detail, but I doubt that a PWM
device has more than one register region.
> + dev_err(&pdev->dev, "no memory resource defined\n");
> + return -ENODEV;
> + }
> +
> + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
> + if (!pc->mmio_base) {
> + dev_err(&pdev->dev, "failed to ioremap() registers\n");
> + return -EADDRNOTAVAIL;
> + }
> +
> + ret = pwmchip_add(&pc->chip);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
> + return ret;
> + }
> +
> + pm_runtime_enable(&pdev->dev);
> + platform_set_drvdata(pdev, pc);
> + dev_info(&pdev->dev, "PWM device initialized\n");
I think this can go away. If none of the above errors is shown you can
just assume that the PWM was initialized.
> + return 0;
> +}
> +
> +static int __devexit ecap_pwm_remove(struct platform_device *pdev)
> +{
> + struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
> + struct pwm_device *pwm;
> +
> + if (WARN_ON(!pc))
> + return -ENODEV;
Do you really want to be this verbose? This kind of check is very rare
anyway, but explicitly warning about it doesn't seems overkill. I think
the check can even be left out, since every possible error that would
lead to the driver data not being checked would already cause .probe()
to return < 0 and therefore .remove() won't ever be called anyway.
> +
> + pwm = &pc->chip.pwms[0];
You don't use this variable.
Thierry
> + pm_runtime_put_sync(&pdev->dev);
> + pm_runtime_disable(&pdev->dev);
> + pwmchip_remove(&pc->chip);
> + return 0;
> +}
> +
> +static struct platform_driver ecap_pwm_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + },
> + .probe = ecap_pwm_probe,
> + .remove = __devexit_p(ecap_pwm_remove),
> +};
> +
> +module_platform_driver(ecap_pwm_driver);
> +
> +MODULE_DESCRIPTION("ECAP PWM driver");
> +MODULE_AUTHOR("Texas Instruments");
> +MODULE_LICENSE("GPL");
> --
> 1.7.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
>
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-23 6:52 ` Thierry Reding
0 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-23 6:52 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> ECAP hardware on AM33XX SOC supports auxiliary PWM (APWM) feature. This
> commit adds PWM driver support for ECAP hardware on AM33XX SOC.
>
> In the ECAP hardware, each PWM pin can also be configured to be in
> capture mode. Current implementation only supports PWM mode of
> operation. Also, hardware supports sync between multiple PWM pins but
> the driver supports simple independent PWM functionality.
>
> Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
> Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
> ---
> :100644 100644 94e176e... f20b8f2... M drivers/pwm/Kconfig
> :100644 100644 5459702... 7dd90ec... M drivers/pwm/Makefile
> :000000 100644 0000000... 81efc9e... A drivers/pwm/pwm-ecap.c
> drivers/pwm/Kconfig | 10 ++
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-ecap.c | 255 ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 266 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 94e176e..f20b8f2 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -85,4 +85,14 @@ config PWM_VT8500
> To compile this driver as a module, choose M here: the module
> will be called pwm-vt8500.
>
> +config PWM_ECAP
> + tristate "ECAP PWM support"
> + depends on SOC_AM33XX
> + help
> + PWM driver support for the ECAP APWM controller found on AM33XX
> + TI SOC
> +
> + To compile this driver as a module, choose M here: the module
> + will be called pwm_ecap.
> +
> endif
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 5459702..7dd90ec 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -7,3 +7,4 @@ obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
> obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
> obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
> obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
> +obj-$(CONFIG_PWM_ECAP) += pwm-ecap.o
Both the Kconfig and Makefile should have the entries ordered
alphabetically.
> diff --git a/drivers/pwm/pwm-ecap.c b/drivers/pwm/pwm-ecap.c
> new file mode 100644
> index 0000000..81efc9e
> --- /dev/null
> +++ b/drivers/pwm/pwm-ecap.c
> @@ -0,0 +1,255 @@
> +/*
> + * ECAP PWM driver
> + *
> + * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/pwm.h>
> +
> +/* ECAP registers and bits definitions */
> +#define TSCTR 0x00
> +#define CTRPHS 0x04
> +#define CAP1 0x08
> +#define CAP2 0x0C
> +#define CAP3 0x10
> +#define CAP4 0x14
> +#define ECCTL1 0x28
These registers are not used. I guess there is some use to list all
registers here but on the other hand the majority are unused so they
just clutter the driver.
> +#define ECCTL2_APWM_POL_LOW BIT(10)
This bit is never used.
> +#define ECEINT 0x2C
> +#define ECFLG 0x2E
> +#define ECCLR 0x30
> +#define REVID 0x5c
These are unused as well.
> +
> +#define DRIVER_NAME "ecap"
You only use this once when defining the struct platform_driver, so
maybe you can just drop it.
> +
> +struct ecap_pwm_chip {
> + struct pwm_chip chip;
> + unsigned int clk_rate;
> + void __iomem *mmio_base;
> + int pwm_period_ns;
> + int pwm_duty_ns;
> +};
The pwm_period_ns and pwm_duty_ns don't seem to be used at all. Can they
be dropped?
> +
> +static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
> +{
> + return container_of(chip, struct ecap_pwm_chip, chip);
> +}
> +
> +/*
> + * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
> + * duty_ns = 10^9 * duty_cycles / PWM_CLK_RATE
> + */
> +static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
> + int duty_ns, int period_ns)
> +{
> + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
> + unsigned long long c;
> + unsigned long period_cycles, duty_cycles;
> + unsigned int reg_val;
> +
> + if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
> + return -ERANGE;
> +
> + c = pc->clk_rate;
> + c = c * period_ns;
> + do_div(c, NSEC_PER_SEC);
> + period_cycles = (unsigned long)c;
> +
> + if (period_cycles < 1) {
> + period_cycles = 1;
> + duty_cycles = 1;
> + } else {
> + c = pc->clk_rate;
> + c = c * duty_ns;
> + do_div(c, NSEC_PER_SEC);
> + duty_cycles = (unsigned long)c;
> + }
> +
> + pc->pwm_duty_ns = duty_ns;
> + pc->pwm_period_ns = period_ns;
> +
> + pm_runtime_get_sync(pc->chip.dev);
> +
> + reg_val = readw(pc->mmio_base + ECCTL2);
> +
> + /* Configure PWM mode & disable sync option */
> + reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
> +
> + writew(reg_val, pc->mmio_base + ECCTL2);
> +
> + if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
> + /* Update active registers if not running */
> + writel(duty_cycles, pc->mmio_base + CAP2);
> + writel(period_cycles, pc->mmio_base + CAP1);
> + } else {
> + /*
> + * Update shadow registers to configure period and
> + * compare values. This helps current PWM period to
> + * complete on reconfiguring
> + */
> + writel(duty_cycles, pc->mmio_base + CAP4);
> + writel(period_cycles, pc->mmio_base + CAP3);
> + }
> +
> + pm_runtime_put_sync(pc->chip.dev);
> + return 0;
> +}
> +
> +static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
> + unsigned int reg_val;
> +
> + /* Leave clock enabled on enabling PWM */
> + pm_runtime_get_sync(pc->chip.dev);
> +
> + /*
> + * Enable 'Free run Time stamp counter mode' to start counter
> + * and 'APWM mode' to enable APWM output
> + */
> + reg_val = readw(pc->mmio_base + ECCTL2);
> + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
You already set the APWM_MODE bit in .config(), why is it needed here
again? Seeing that .disable() clears the bit as well, perhaps leaving it
clear in .config() is the better option.
> + writew(reg_val, pc->mmio_base + ECCTL2);
> + return 0;
> +}
> +
> +static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
> + unsigned int reg_val;
> +
> + /*
> + * Disable 'Free run Time stamp counter mode' to stop counter
> + * and 'APWM mode' to put APWM output to low
> + */
> + reg_val = readw(pc->mmio_base + ECCTL2);
> + reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
> + writew(reg_val, pc->mmio_base + ECCTL2);
> +
> + /* Disable clock on PWM disable */
> + pm_runtime_put_sync(pc->chip.dev);
> +}
> +
> +static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + if (test_bit(PWMF_ENABLED, &pwm->flags)) {
> + dev_warn(chip->dev, "Removing PWM device without disabling\n");
> + pm_runtime_put_sync(chip->dev);
> + }
> +}
I wonder whether we want to have something like this in the PWM core at
some point. Maybe we should call .disable() automatically when they are
freed, or alternatively return -EBUSY if a PWM is freed but still
enabled. I think I prefer the latter. For now we can leave this in,
because I don't want to make any such core changes for 3.6 now that the
merge window is open.
> +
> +static struct pwm_ops ecap_pwm_ops = {
> + .free = ecap_pwm_free,
> + .config = ecap_pwm_config,
> + .enable = ecap_pwm_enable,
> + .disable = ecap_pwm_disable,
> + .owner = THIS_MODULE,
> +};
This should be "static const pwm_ops ...".
> +
> +static int __devinit ecap_pwm_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct resource *r;
> + struct clk *clk;
> + struct ecap_pwm_chip *pc;
> +
> + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
> + if (!pc) {
> + dev_err(&pdev->dev, "failed to allocate memory\n");
> + return -ENOMEM;
> + }
> +
> + clk = devm_clk_get(&pdev->dev, "fck");
> + if (IS_ERR(clk)) {
> + dev_err(&pdev->dev, "failed to get clock\n");
> + return PTR_ERR(clk);
> + }
> +
> + pc->clk_rate = clk_get_rate(clk);
> + if (!pc->clk_rate) {
> + dev_err(&pdev->dev, "failed to get clock rate\n");
> + return -EINVAL;
> + }
> +
> + pc->chip.dev = &pdev->dev;
> + pc->chip.ops = &ecap_pwm_ops;
> + pc->chip.base = -1;
> + pc->chip.npwm = 1;
The cover letter mentions that the AM335x has 3 instances of the ECAP.
Is there any chance that they can be unified in one driver instance
(i.e. .npwm = 3?).
> +
> + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> + if (r == NULL) {
You use "if (!ptr)" everywhere else, maybe you should make this
consistent? Also the platform_get_resource_byname() is really only
useful for devices that have several register regions associated with
them. I don't know your hardware in detail, but I doubt that a PWM
device has more than one register region.
> + dev_err(&pdev->dev, "no memory resource defined\n");
> + return -ENODEV;
> + }
> +
> + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
> + if (!pc->mmio_base) {
> + dev_err(&pdev->dev, "failed to ioremap() registers\n");
> + return -EADDRNOTAVAIL;
> + }
> +
> + ret = pwmchip_add(&pc->chip);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
> + return ret;
> + }
> +
> + pm_runtime_enable(&pdev->dev);
> + platform_set_drvdata(pdev, pc);
> + dev_info(&pdev->dev, "PWM device initialized\n");
I think this can go away. If none of the above errors is shown you can
just assume that the PWM was initialized.
> + return 0;
> +}
> +
> +static int __devexit ecap_pwm_remove(struct platform_device *pdev)
> +{
> + struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
> + struct pwm_device *pwm;
> +
> + if (WARN_ON(!pc))
> + return -ENODEV;
Do you really want to be this verbose? This kind of check is very rare
anyway, but explicitly warning about it doesn't seems overkill. I think
the check can even be left out, since every possible error that would
lead to the driver data not being checked would already cause .probe()
to return < 0 and therefore .remove() won't ever be called anyway.
> +
> + pwm = &pc->chip.pwms[0];
You don't use this variable.
Thierry
> + pm_runtime_put_sync(&pdev->dev);
> + pm_runtime_disable(&pdev->dev);
> + pwmchip_remove(&pc->chip);
> + return 0;
> +}
> +
> +static struct platform_driver ecap_pwm_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + },
> + .probe = ecap_pwm_probe,
> + .remove = __devexit_p(ecap_pwm_remove),
> +};
> +
> +module_platform_driver(ecap_pwm_driver);
> +
> +MODULE_DESCRIPTION("ECAP PWM driver");
> +MODULE_AUTHOR("Texas Instruments");
> +MODULE_LICENSE("GPL");
> --
> 1.7.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120723/75e99371/attachment-0001.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 2/2] PWM: EHRPWM: PWM driver support for EHRPWM.
2012-07-13 9:35 ` Philip, Avinash
@ 2012-07-23 7:42 ` Thierry Reding
-1 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-23 7:42 UTC (permalink / raw)
To: Philip, Avinash; +Cc: linux-arm-kernel, linux-omap
[-- Attachment #1: Type: text/plain, Size: 2445 bytes --]
On Fri, Jul 13, 2012 at 03:05:02PM +0530, Philip, Avinash wrote:
> Enhanced high resolution PWM module (EHRPWM) hardware can be used to
> generate PWM output over 2 channels. This commit adds PWM driver support
> for EHRPWM device present on AM33XX SOC. Current implementation supports
> simple PWM functionality.
>
> Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
> Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
So this driver is very similar to the ECAP one and pretty much all the
comments apply to this as well. Some additional comments below.
> ---
> :100644 100644 f20b8f2... ad62d7a... M drivers/pwm/Kconfig
> :100644 100644 7dd90ec... 636a1d6... M drivers/pwm/Makefile
> :000000 100644 0000000... 985d334... A drivers/pwm/pwm-ehrpwm.c
> drivers/pwm/Kconfig | 10 +
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-ehrpwm.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 487 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index f20b8f2..ad62d7a 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -95,4 +95,14 @@ config PWM_ECAP
> To compile this driver as a module, choose M here: the module
> will be called pwm_ecap.
>
> +config PWM_EHRPWM
> + tristate "EHRPWM PWM support"
> + depends on SOC_AM33XX
> + help
> + PWM driver support for the EHRPWM controller found on AM33XX
> + TI SOC
> +
> + To compile this driver as a module, choose M here: the module
> + will be called pwm-ehrpwm.
> +
Maybe it would be useful to prefix the names with AM33XX here. ECAP and
EHRPWM are sort of generic and may have name clashes in the future.
> +#define PWM_CHANNEL 2 /* EHRPWM channels */
I'd say you can just replace the one occurrence of this with the literal
2. If you still want to have the symbolic name, then I'd suggest to call
it something like NUM_PWM_CHANNELS to make its meaning more obvious.
> +static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev)
> +{
> + struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev);
> +
> + if (WARN_ON(!pc))
> + return -ENODEV;
> +
> + pm_runtime_disable(&pdev->dev);
> + pwmchip_remove(&pc->chip);
> + return 0;
> +}
I forgot to mention this for ECAP, but you need to check the return
value of pwmchip_remove() because there are situations where it can
actually fail.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/2] PWM: EHRPWM: PWM driver support for EHRPWM.
@ 2012-07-23 7:42 ` Thierry Reding
0 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-23 7:42 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Jul 13, 2012 at 03:05:02PM +0530, Philip, Avinash wrote:
> Enhanced high resolution PWM module (EHRPWM) hardware can be used to
> generate PWM output over 2 channels. This commit adds PWM driver support
> for EHRPWM device present on AM33XX SOC. Current implementation supports
> simple PWM functionality.
>
> Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
> Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
So this driver is very similar to the ECAP one and pretty much all the
comments apply to this as well. Some additional comments below.
> ---
> :100644 100644 f20b8f2... ad62d7a... M drivers/pwm/Kconfig
> :100644 100644 7dd90ec... 636a1d6... M drivers/pwm/Makefile
> :000000 100644 0000000... 985d334... A drivers/pwm/pwm-ehrpwm.c
> drivers/pwm/Kconfig | 10 +
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-ehrpwm.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 487 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index f20b8f2..ad62d7a 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -95,4 +95,14 @@ config PWM_ECAP
> To compile this driver as a module, choose M here: the module
> will be called pwm_ecap.
>
> +config PWM_EHRPWM
> + tristate "EHRPWM PWM support"
> + depends on SOC_AM33XX
> + help
> + PWM driver support for the EHRPWM controller found on AM33XX
> + TI SOC
> +
> + To compile this driver as a module, choose M here: the module
> + will be called pwm-ehrpwm.
> +
Maybe it would be useful to prefix the names with AM33XX here. ECAP and
EHRPWM are sort of generic and may have name clashes in the future.
> +#define PWM_CHANNEL 2 /* EHRPWM channels */
I'd say you can just replace the one occurrence of this with the literal
2. If you still want to have the symbolic name, then I'd suggest to call
it something like NUM_PWM_CHANNELS to make its meaning more obvious.
> +static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev)
> +{
> + struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev);
> +
> + if (WARN_ON(!pc))
> + return -ENODEV;
> +
> + pm_runtime_disable(&pdev->dev);
> + pwmchip_remove(&pc->chip);
> + return 0;
> +}
I forgot to mention this for ECAP, but you need to check the return
value of pwmchip_remove() because there are situations where it can
actually fail.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120723/9ecdce34/attachment.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* RE: [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-23 6:52 ` Thierry Reding
@ 2012-07-23 9:10 ` Philip, Avinash
-1 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-23 9:10 UTC (permalink / raw)
To: Thierry Reding; +Cc: linux-arm-kernel, linux-omap
On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
[snip]
> > obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
> > +obj-$(CONFIG_PWM_ECAP) += pwm-ecap.o
>
> Both the Kconfig and Makefile should have the entries ordered
> alphabetically.
Ok. I will correct it.
>
[snip]
> > +/* ECAP registers and bits definitions */
> > +#define TSCTR 0x00
> > +#define CTRPHS 0x04
> > +#define CAP1 0x08
> > +#define CAP2 0x0C
> > +#define CAP3 0x10
> > +#define CAP4 0x14
> > +#define ECCTL1 0x28
>
> These registers are not used. I guess there is some use to list all
> registers here but on the other hand the majority are unused so they
> just clutter the driver.
>
> > +#define ECCTL2_APWM_POL_LOW BIT(10)
>
> This bit is never used.
>
> > +#define ECEINT 0x2C
> > +#define ECFLG 0x2E
> > +#define ECCLR 0x30
> > +#define REVID 0x5c
>
> These are unused as well.
Ok. I will remove it. These are been placed in future enhancement.
>
> > +
> > +#define DRIVER_NAME "ecap"
>
> You only use this once when defining the struct platform_driver, so
> maybe you can just drop it.
In the v2 patch, I am planning to use same in
platform_get_resource_byname(). Here I will use this define to search
resources.
>
> > +
> > +struct ecap_pwm_chip {
> > + struct pwm_chip chip;
> > + unsigned int clk_rate;
> > + void __iomem *mmio_base;
> > + int pwm_period_ns;
> > + int pwm_duty_ns;
> > +};
>
> The pwm_period_ns and pwm_duty_ns don't seem to be used at all. Can they
> be dropped?
Ok. I will remove it.
>
> > + /*
> > + * Enable 'Free run Time stamp counter mode' to start counter
> > + * and 'APWM mode' to enable APWM output
> > + */
> > + reg_val = readw(pc->mmio_base + ECCTL2);
> > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
>
> You already set the APWM_MODE bit in .config(), why is it needed here
> again? Seeing that .disable() clears the bit as well, perhaps leaving it
> clear in .config() is the better option.
Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
in idle state).
The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
During PWM configuration. To enable loading from Shadow registers, APWM mode
should be set.
The same is done in .config().
>
> > +}
> > +
> > +static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
> > +{
> > + if (test_bit(PWMF_ENABLED, &pwm->flags)) {
> > + dev_warn(chip->dev, "Removing PWM device without disabling\n");
> > + pm_runtime_put_sync(chip->dev);
> > + }
> > +}
>
> I wonder whether we want to have something like this in the PWM core at
> some point. Maybe we should call .disable() automatically when they are
> freed, or alternatively return -EBUSY if a PWM is freed but still
> enabled. I think I prefer the latter. For now we can leave this in,
> because I don't want to make any such core changes for 3.6 now that the
> merge window is open.
OK Thanks.
>
> > +
> > +static struct pwm_ops ecap_pwm_ops = {
> > + .free = ecap_pwm_free,
> > + .config = ecap_pwm_config,
> > + .enable = ecap_pwm_enable,
> > + .disable = ecap_pwm_disable,
> > + .owner = THIS_MODULE,
> > +};
>
> This should be "static const pwm_ops ...".
Ok I will correct it.
>
> > +
> > +static int __devinit ecap_pwm_probe(struct platform_device *pdev)
> > +{
> > + int ret;
> > + struct resource *r;
> > + struct clk *clk;
> > + struct ecap_pwm_chip *pc;
> > +
> > + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
> > + if (!pc) {
> > + dev_err(&pdev->dev, "failed to allocate memory\n");
> > + return -ENOMEM;
> > + }
> > +
> > + clk = devm_clk_get(&pdev->dev, "fck");
> > + if (IS_ERR(clk)) {
> > + dev_err(&pdev->dev, "failed to get clock\n");
> > + return PTR_ERR(clk);
> > + }
> > +
> > + pc->clk_rate = clk_get_rate(clk);
> > + if (!pc->clk_rate) {
> > + dev_err(&pdev->dev, "failed to get clock rate\n");
> > + return -EINVAL;
> > + }
> > +
> > + pc->chip.dev = &pdev->dev;
> > + pc->chip.ops = &ecap_pwm_ops;
> > + pc->chip.base = -1;
> > + pc->chip.npwm = 1;
>
> The cover letter mentions that the AM335x has 3 instances of the ECAP.
> Is there any chance that they can be unified in one driver instance
> (i.e. .npwm = 3?).
No. All instances have separate resources (clocks, memory ..).
>
> > +
> > + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> > + if (r == NULL) {
>
> You use "if (!ptr)" everywhere else, maybe you should make this
> consistent?
Ok
> Also the platform_get_resource_byname() is really only
> useful for devices that have several register regions associated with
> them. I don't know your hardware in detail, but I doubt that a PWM
> device has more than one register region.
In fact PWM Module has 4 different register space (eCAP, eHRPWM, eQEP &
Common Config space). So I need to use platform_get_resource_byname()
>
> > + dev_err(&pdev->dev, "no memory resource defined\n");
> > + return -ENODEV;
> > + }
> > +
> > + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
> > + if (!pc->mmio_base) {
> > + dev_err(&pdev->dev, "failed to ioremap() registers\n");
> > + return -EADDRNOTAVAIL;
> > + }
> > +
> > + ret = pwmchip_add(&pc->chip);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + pm_runtime_enable(&pdev->dev);
> > + platform_set_drvdata(pdev, pc);
> > + dev_info(&pdev->dev, "PWM device initialized\n");
>
> I think this can go away. If none of the above errors is shown you can
> just assume that the PWM was initialized.
Ok. I will remove.
>
> > + return 0;
> > +}
> > +
> > +static int __devexit ecap_pwm_remove(struct platform_device *pdev)
> > +{
> > + struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
> > + struct pwm_device *pwm;
> > +
> > + if (WARN_ON(!pc))
> > + return -ENODEV;
>
> Do you really want to be this verbose? This kind of check is very rare
> anyway, but explicitly warning about it doesn't seems overkill. I think
> the check can even be left out, since every possible error that would
> lead to the driver data not being checked would already cause .probe()
> to return < 0 and therefore .remove() won't ever be called anyway.
Point taken, I will remove.
>
> > +
> > + pwm = &pc->chip.pwms[0];
>
> You don't use this variable.
Ok
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-23 9:10 ` Philip, Avinash
0 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-23 9:10 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
[snip]
> > obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
> > +obj-$(CONFIG_PWM_ECAP) += pwm-ecap.o
>
> Both the Kconfig and Makefile should have the entries ordered
> alphabetically.
Ok. I will correct it.
>
[snip]
> > +/* ECAP registers and bits definitions */
> > +#define TSCTR 0x00
> > +#define CTRPHS 0x04
> > +#define CAP1 0x08
> > +#define CAP2 0x0C
> > +#define CAP3 0x10
> > +#define CAP4 0x14
> > +#define ECCTL1 0x28
>
> These registers are not used. I guess there is some use to list all
> registers here but on the other hand the majority are unused so they
> just clutter the driver.
>
> > +#define ECCTL2_APWM_POL_LOW BIT(10)
>
> This bit is never used.
>
> > +#define ECEINT 0x2C
> > +#define ECFLG 0x2E
> > +#define ECCLR 0x30
> > +#define REVID 0x5c
>
> These are unused as well.
Ok. I will remove it. These are been placed in future enhancement.
>
> > +
> > +#define DRIVER_NAME "ecap"
>
> You only use this once when defining the struct platform_driver, so
> maybe you can just drop it.
In the v2 patch, I am planning to use same in
platform_get_resource_byname(). Here I will use this define to search
resources.
>
> > +
> > +struct ecap_pwm_chip {
> > + struct pwm_chip chip;
> > + unsigned int clk_rate;
> > + void __iomem *mmio_base;
> > + int pwm_period_ns;
> > + int pwm_duty_ns;
> > +};
>
> The pwm_period_ns and pwm_duty_ns don't seem to be used at all. Can they
> be dropped?
Ok. I will remove it.
>
> > + /*
> > + * Enable 'Free run Time stamp counter mode' to start counter
> > + * and 'APWM mode' to enable APWM output
> > + */
> > + reg_val = readw(pc->mmio_base + ECCTL2);
> > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
>
> You already set the APWM_MODE bit in .config(), why is it needed here
> again? Seeing that .disable() clears the bit as well, perhaps leaving it
> clear in .config() is the better option.
Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
in idle state).
The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
During PWM configuration. To enable loading from Shadow registers, APWM mode
should be set.
The same is done in .config().
>
> > +}
> > +
> > +static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
> > +{
> > + if (test_bit(PWMF_ENABLED, &pwm->flags)) {
> > + dev_warn(chip->dev, "Removing PWM device without disabling\n");
> > + pm_runtime_put_sync(chip->dev);
> > + }
> > +}
>
> I wonder whether we want to have something like this in the PWM core at
> some point. Maybe we should call .disable() automatically when they are
> freed, or alternatively return -EBUSY if a PWM is freed but still
> enabled. I think I prefer the latter. For now we can leave this in,
> because I don't want to make any such core changes for 3.6 now that the
> merge window is open.
OK Thanks.
>
> > +
> > +static struct pwm_ops ecap_pwm_ops = {
> > + .free = ecap_pwm_free,
> > + .config = ecap_pwm_config,
> > + .enable = ecap_pwm_enable,
> > + .disable = ecap_pwm_disable,
> > + .owner = THIS_MODULE,
> > +};
>
> This should be "static const pwm_ops ...".
Ok I will correct it.
>
> > +
> > +static int __devinit ecap_pwm_probe(struct platform_device *pdev)
> > +{
> > + int ret;
> > + struct resource *r;
> > + struct clk *clk;
> > + struct ecap_pwm_chip *pc;
> > +
> > + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
> > + if (!pc) {
> > + dev_err(&pdev->dev, "failed to allocate memory\n");
> > + return -ENOMEM;
> > + }
> > +
> > + clk = devm_clk_get(&pdev->dev, "fck");
> > + if (IS_ERR(clk)) {
> > + dev_err(&pdev->dev, "failed to get clock\n");
> > + return PTR_ERR(clk);
> > + }
> > +
> > + pc->clk_rate = clk_get_rate(clk);
> > + if (!pc->clk_rate) {
> > + dev_err(&pdev->dev, "failed to get clock rate\n");
> > + return -EINVAL;
> > + }
> > +
> > + pc->chip.dev = &pdev->dev;
> > + pc->chip.ops = &ecap_pwm_ops;
> > + pc->chip.base = -1;
> > + pc->chip.npwm = 1;
>
> The cover letter mentions that the AM335x has 3 instances of the ECAP.
> Is there any chance that they can be unified in one driver instance
> (i.e. .npwm = 3?).
No. All instances have separate resources (clocks, memory ..).
>
> > +
> > + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> > + if (r == NULL) {
>
> You use "if (!ptr)" everywhere else, maybe you should make this
> consistent?
Ok
> Also the platform_get_resource_byname() is really only
> useful for devices that have several register regions associated with
> them. I don't know your hardware in detail, but I doubt that a PWM
> device has more than one register region.
In fact PWM Module has 4 different register space (eCAP, eHRPWM, eQEP &
Common Config space). So I need to use platform_get_resource_byname()
>
> > + dev_err(&pdev->dev, "no memory resource defined\n");
> > + return -ENODEV;
> > + }
> > +
> > + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
> > + if (!pc->mmio_base) {
> > + dev_err(&pdev->dev, "failed to ioremap() registers\n");
> > + return -EADDRNOTAVAIL;
> > + }
> > +
> > + ret = pwmchip_add(&pc->chip);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + pm_runtime_enable(&pdev->dev);
> > + platform_set_drvdata(pdev, pc);
> > + dev_info(&pdev->dev, "PWM device initialized\n");
>
> I think this can go away. If none of the above errors is shown you can
> just assume that the PWM was initialized.
Ok. I will remove.
>
> > + return 0;
> > +}
> > +
> > +static int __devexit ecap_pwm_remove(struct platform_device *pdev)
> > +{
> > + struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
> > + struct pwm_device *pwm;
> > +
> > + if (WARN_ON(!pc))
> > + return -ENODEV;
>
> Do you really want to be this verbose? This kind of check is very rare
> anyway, but explicitly warning about it doesn't seems overkill. I think
> the check can even be left out, since every possible error that would
> lead to the driver data not being checked would already cause .probe()
> to return < 0 and therefore .remove() won't ever be called anyway.
Point taken, I will remove.
>
> > +
> > + pwm = &pc->chip.pwms[0];
>
> You don't use this variable.
Ok
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* RE: [PATCH 2/2] PWM: EHRPWM: PWM driver support for EHRPWM.
2012-07-23 7:42 ` Thierry Reding
@ 2012-07-23 9:16 ` Philip, Avinash
-1 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-23 9:16 UTC (permalink / raw)
To: Thierry Reding; +Cc: linux-arm-kernel, linux-omap
On Mon, Jul 23, 2012 at 13:12:21, Thierry Reding wrote:
> On Fri, Jul 13, 2012 at 03:05:02PM +0530, Philip, Avinash wrote:
> > Enhanced high resolution PWM module (EHRPWM) hardware can be used to
> > generate PWM output over 2 channels. This commit adds PWM driver support
> > for EHRPWM device present on AM33XX SOC. Current implementation supports
> > simple PWM functionality.
> >
> > Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
> > Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>
> So this driver is very similar to the ECAP one and pretty much all the
> comments apply to this as well. Some additional comments below.
Ok I will correct it for all the comments.
>
> > ---
[snip]
> > +
> > + To compile this driver as a module, choose M here: the module
> > + will be called pwm-ehrpwm.
> > +
>
> Maybe it would be useful to prefix the names with AM33XX here. ECAP and
> EHRPWM are sort of generic and may have name clashes in the future.
Ok, I will make us TI prefix as davinci platform also uses same modules.
>
> > +#define PWM_CHANNEL 2 /* EHRPWM channels */
>
> I'd say you can just replace the one occurrence of this with the literal
> 2. If you still want to have the symbolic name, then I'd suggest to call
> it something like NUM_PWM_CHANNELS to make its meaning more obvious.
I will correct it as NUM_PWM_CHANNELS.
>
> > +static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev)
> > +{
> > + struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev);
> > +
> > + if (WARN_ON(!pc))
> > + return -ENODEV;
> > +
> > + pm_runtime_disable(&pdev->dev);
> > + pwmchip_remove(&pc->chip);
> > + return 0;
> > +}
>
> I forgot to mention this for ECAP, but you need to check the return
> value of pwmchip_remove() because there are situations where it can
> actually fail.
I will correct it.
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/2] PWM: EHRPWM: PWM driver support for EHRPWM.
@ 2012-07-23 9:16 ` Philip, Avinash
0 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-23 9:16 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jul 23, 2012 at 13:12:21, Thierry Reding wrote:
> On Fri, Jul 13, 2012 at 03:05:02PM +0530, Philip, Avinash wrote:
> > Enhanced high resolution PWM module (EHRPWM) hardware can be used to
> > generate PWM output over 2 channels. This commit adds PWM driver support
> > for EHRPWM device present on AM33XX SOC. Current implementation supports
> > simple PWM functionality.
> >
> > Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
> > Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
>
> So this driver is very similar to the ECAP one and pretty much all the
> comments apply to this as well. Some additional comments below.
Ok I will correct it for all the comments.
>
> > ---
[snip]
> > +
> > + To compile this driver as a module, choose M here: the module
> > + will be called pwm-ehrpwm.
> > +
>
> Maybe it would be useful to prefix the names with AM33XX here. ECAP and
> EHRPWM are sort of generic and may have name clashes in the future.
Ok, I will make us TI prefix as davinci platform also uses same modules.
>
> > +#define PWM_CHANNEL 2 /* EHRPWM channels */
>
> I'd say you can just replace the one occurrence of this with the literal
> 2. If you still want to have the symbolic name, then I'd suggest to call
> it something like NUM_PWM_CHANNELS to make its meaning more obvious.
I will correct it as NUM_PWM_CHANNELS.
>
> > +static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev)
> > +{
> > + struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev);
> > +
> > + if (WARN_ON(!pc))
> > + return -ENODEV;
> > +
> > + pm_runtime_disable(&pdev->dev);
> > + pwmchip_remove(&pc->chip);
> > + return 0;
> > +}
>
> I forgot to mention this for ECAP, but you need to check the return
> value of pwmchip_remove() because there are situations where it can
> actually fail.
I will correct it.
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-23 9:10 ` Philip, Avinash
@ 2012-07-23 9:22 ` Thierry Reding
-1 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-23 9:22 UTC (permalink / raw)
To: Philip, Avinash; +Cc: linux-arm-kernel, linux-omap
[-- Attachment #1: Type: text/plain, Size: 2686 bytes --]
On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > + /*
> > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > + * and 'APWM mode' to enable APWM output
> > > + */
> > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> >
> > You already set the APWM_MODE bit in .config(), why is it needed here
> > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > clear in .config() is the better option.
>
> Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> in idle state).
>
> The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> During PWM configuration. To enable loading from Shadow registers, APWM mode
> should be set.
> The same is done in .config().
My point was that if you do it in .enable(), then why do you still set
it in .config()? Since the sequence is typically .config() followed by
.enable(), setting the bit in both seems redundant. It should be enough
to load the registers during .enable(), right?
> > > + pc->chip.dev = &pdev->dev;
> > > + pc->chip.ops = &ecap_pwm_ops;
> > > + pc->chip.base = -1;
> > > + pc->chip.npwm = 1;
> >
> > The cover letter mentions that the AM335x has 3 instances of the ECAP.
> > Is there any chance that they can be unified in one driver instance
> > (i.e. .npwm = 3?).
>
> No. All instances have separate resources (clocks, memory ..).
>
> >
> > > +
> > > + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> > > + if (r == NULL) {
> >
> > You use "if (!ptr)" everywhere else, maybe you should make this
> > consistent?
> Ok
> > Also the platform_get_resource_byname() is really only
> > useful for devices that have several register regions associated with
> > them. I don't know your hardware in detail, but I doubt that a PWM
> > device has more than one register region.
>
> In fact PWM Module has 4 different register space (eCAP, eHRPWM, eQEP &
> Common Config space). So I need to use platform_get_resource_byname()
Above you say that all instances have separate resources, so how come
you need to specify 4 register spaces? The eCAP registers should clearly
be passed to the eCAP device, while the eHRPWM registers should go to
the eHRPWM device.
My point is that if you need to refer to the register region by name,
then the driver should clearly be using more than a single region.
Neither the eCAP nor eHRPWM do that.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-23 9:22 ` Thierry Reding
0 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-23 9:22 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > + /*
> > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > + * and 'APWM mode' to enable APWM output
> > > + */
> > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> >
> > You already set the APWM_MODE bit in .config(), why is it needed here
> > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > clear in .config() is the better option.
>
> Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> in idle state).
>
> The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> During PWM configuration. To enable loading from Shadow registers, APWM mode
> should be set.
> The same is done in .config().
My point was that if you do it in .enable(), then why do you still set
it in .config()? Since the sequence is typically .config() followed by
.enable(), setting the bit in both seems redundant. It should be enough
to load the registers during .enable(), right?
> > > + pc->chip.dev = &pdev->dev;
> > > + pc->chip.ops = &ecap_pwm_ops;
> > > + pc->chip.base = -1;
> > > + pc->chip.npwm = 1;
> >
> > The cover letter mentions that the AM335x has 3 instances of the ECAP.
> > Is there any chance that they can be unified in one driver instance
> > (i.e. .npwm = 3?).
>
> No. All instances have separate resources (clocks, memory ..).
>
> >
> > > +
> > > + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> > > + if (r == NULL) {
> >
> > You use "if (!ptr)" everywhere else, maybe you should make this
> > consistent?
> Ok
> > Also the platform_get_resource_byname() is really only
> > useful for devices that have several register regions associated with
> > them. I don't know your hardware in detail, but I doubt that a PWM
> > device has more than one register region.
>
> In fact PWM Module has 4 different register space (eCAP, eHRPWM, eQEP &
> Common Config space). So I need to use platform_get_resource_byname()
Above you say that all instances have separate resources, so how come
you need to specify 4 register spaces? The eCAP registers should clearly
be passed to the eCAP device, while the eHRPWM registers should go to
the eHRPWM device.
My point is that if you need to refer to the register region by name,
then the driver should clearly be using more than a single region.
Neither the eCAP nor eHRPWM do that.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120723/768d1fc6/attachment.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* RE: [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-23 9:22 ` Thierry Reding
@ 2012-07-23 12:44 ` Philip, Avinash
-1 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-23 12:44 UTC (permalink / raw)
To: Thierry Reding; +Cc: linux-arm-kernel, linux-omap
On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > + /*
> > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > + * and 'APWM mode' to enable APWM output
> > > > + */
> > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > >
> > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > clear in .config() is the better option.
> >
> > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > in idle state).
> >
> > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > should be set.
> > The same is done in .config().
>
> My point was that if you do it in .enable(), then why do you still set
> it in .config()? Since the sequence is typically .config() followed by
> .enable(), setting the bit in both seems redundant. It should be enough
> to load the registers during .enable(), right?
Consider scenarios where .enable() can be called without calling .config().
Example I just need to stop the pwm signal momentarily and re-enable.
In this case, .config() need not be called. So, APWM mode bit needs to be
set in .enable()
Now, considering .config() followed by .enable().
Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
mode bit to be set.
The same can be done in .enable() also. However, we again need to pass
the pwm parameters (period & duty cycle values) to the .enable().
We don't want to duplicate this parameter passing.
To avoid this we enable the APWM mode bit in both .config() & .enable().
>
> > > > + pc->chip.dev = &pdev->dev;
> > > > + pc->chip.ops = &ecap_pwm_ops;
> > > > + pc->chip.base = -1;
> > > > + pc->chip.npwm = 1;
> > >
> > > The cover letter mentions that the AM335x has 3 instances of the ECAP.
> > > Is there any chance that they can be unified in one driver instance
> > > (i.e. .npwm = 3?).
> >
> > No. All instances have separate resources (clocks, memory ..).
> >
> > >
> > > > +
> > > > + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> > > > + if (r == NULL) {
> > >
> > > You use "if (!ptr)" everywhere else, maybe you should make this
> > > consistent?
> > Ok
> > > Also the platform_get_resource_byname() is really only
> > > useful for devices that have several register regions associated with
> > > them. I don't know your hardware in detail, but I doubt that a PWM
> > > device has more than one register region.
> >
> > In fact PWM Module has 4 different register space (eCAP, eHRPWM, eQEP &
> > Common Config space). So I need to use platform_get_resource_byname()
>
> Above you say that all instances have separate resources, so how come
> you need to specify 4 register spaces? The eCAP registers should clearly
> be passed to the eCAP device, while the eHRPWM registers should go to
> the eHRPWM device.
>
> My point is that if you need to refer to the register region by name,
> then the driver should clearly be using more than a single region.
> Neither the eCAP nor eHRPWM do that.
On AM335x SoC, this common config space is shared by the other 3
modules (eCAP, eHRPWM, eQEP).
However, on Davinci platform don't have any common config space.
So from driver reusability view, usage of platform_get_resource_byname()
is better choice.
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-23 12:44 ` Philip, Avinash
0 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-23 12:44 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > + /*
> > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > + * and 'APWM mode' to enable APWM output
> > > > + */
> > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > >
> > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > clear in .config() is the better option.
> >
> > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > in idle state).
> >
> > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > should be set.
> > The same is done in .config().
>
> My point was that if you do it in .enable(), then why do you still set
> it in .config()? Since the sequence is typically .config() followed by
> .enable(), setting the bit in both seems redundant. It should be enough
> to load the registers during .enable(), right?
Consider scenarios where .enable() can be called without calling .config().
Example I just need to stop the pwm signal momentarily and re-enable.
In this case, .config() need not be called. So, APWM mode bit needs to be
set in .enable()
Now, considering .config() followed by .enable().
Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
mode bit to be set.
The same can be done in .enable() also. However, we again need to pass
the pwm parameters (period & duty cycle values) to the .enable().
We don't want to duplicate this parameter passing.
To avoid this we enable the APWM mode bit in both .config() & .enable().
>
> > > > + pc->chip.dev = &pdev->dev;
> > > > + pc->chip.ops = &ecap_pwm_ops;
> > > > + pc->chip.base = -1;
> > > > + pc->chip.npwm = 1;
> > >
> > > The cover letter mentions that the AM335x has 3 instances of the ECAP.
> > > Is there any chance that they can be unified in one driver instance
> > > (i.e. .npwm = 3?).
> >
> > No. All instances have separate resources (clocks, memory ..).
> >
> > >
> > > > +
> > > > + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> > > > + if (r == NULL) {
> > >
> > > You use "if (!ptr)" everywhere else, maybe you should make this
> > > consistent?
> > Ok
> > > Also the platform_get_resource_byname() is really only
> > > useful for devices that have several register regions associated with
> > > them. I don't know your hardware in detail, but I doubt that a PWM
> > > device has more than one register region.
> >
> > In fact PWM Module has 4 different register space (eCAP, eHRPWM, eQEP &
> > Common Config space). So I need to use platform_get_resource_byname()
>
> Above you say that all instances have separate resources, so how come
> you need to specify 4 register spaces? The eCAP registers should clearly
> be passed to the eCAP device, while the eHRPWM registers should go to
> the eHRPWM device.
>
> My point is that if you need to refer to the register region by name,
> then the driver should clearly be using more than a single region.
> Neither the eCAP nor eHRPWM do that.
On AM335x SoC, this common config space is shared by the other 3
modules (eCAP, eHRPWM, eQEP).
However, on Davinci platform don't have any common config space.
So from driver reusability view, usage of platform_get_resource_byname()
is better choice.
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-23 12:44 ` Philip, Avinash
@ 2012-07-23 13:37 ` Thierry Reding
-1 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-23 13:37 UTC (permalink / raw)
To: Philip, Avinash; +Cc: linux-omap, linux-arm-kernel
[-- Attachment #1: Type: text/plain, Size: 4704 bytes --]
On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > + /*
> > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > + * and 'APWM mode' to enable APWM output
> > > > > + */
> > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > >
> > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > clear in .config() is the better option.
> > >
> > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > in idle state).
> > >
> > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > should be set.
> > > The same is done in .config().
> >
> > My point was that if you do it in .enable(), then why do you still set
> > it in .config()? Since the sequence is typically .config() followed by
> > .enable(), setting the bit in both seems redundant. It should be enough
> > to load the registers during .enable(), right?
>
> Consider scenarios where .enable() can be called without calling .config().
> Example I just need to stop the pwm signal momentarily and re-enable.
> In this case, .config() need not be called. So, APWM mode bit needs to be
> set in .enable()
>
> Now, considering .config() followed by .enable().
> Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> mode bit to be set.
>
> The same can be done in .enable() also. However, we again need to pass
> the pwm parameters (period & duty cycle values) to the .enable().
> We don't want to duplicate this parameter passing.
> To avoid this we enable the APWM mode bit in both .config() & .enable().
That's weird. I assumed that the values written to the shadow registers
would automatically be copied to the active registers on each new PWM
period. If I understand correctly what you're saying, however, the eCAP
requires the APWM bit to be set in order to write the shadow registers
at all.
> > > > > + pc->chip.dev = &pdev->dev;
> > > > > + pc->chip.ops = &ecap_pwm_ops;
> > > > > + pc->chip.base = -1;
> > > > > + pc->chip.npwm = 1;
> > > >
> > > > The cover letter mentions that the AM335x has 3 instances of the ECAP.
> > > > Is there any chance that they can be unified in one driver instance
> > > > (i.e. .npwm = 3?).
> > >
> > > No. All instances have separate resources (clocks, memory ..).
> > >
> > > >
> > > > > +
> > > > > + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> > > > > + if (r == NULL) {
> > > >
> > > > You use "if (!ptr)" everywhere else, maybe you should make this
> > > > consistent?
> > > Ok
> > > > Also the platform_get_resource_byname() is really only
> > > > useful for devices that have several register regions associated with
> > > > them. I don't know your hardware in detail, but I doubt that a PWM
> > > > device has more than one register region.
> > >
> > > In fact PWM Module has 4 different register space (eCAP, eHRPWM, eQEP &
> > > Common Config space). So I need to use platform_get_resource_byname()
> >
> > Above you say that all instances have separate resources, so how come
> > you need to specify 4 register spaces? The eCAP registers should clearly
> > be passed to the eCAP device, while the eHRPWM registers should go to
> > the eHRPWM device.
> >
> > My point is that if you need to refer to the register region by name,
> > then the driver should clearly be using more than a single region.
> > Neither the eCAP nor eHRPWM do that.
>
> On AM335x SoC, this common config space is shared by the other 3
> modules (eCAP, eHRPWM, eQEP).
> However, on Davinci platform don't have any common config space.
>
> So from driver reusability view, usage of platform_get_resource_byname()
> is better choice.
I don't quite see how you would be able to reuse the driver in that
case. The driver that you posted uses only one memory region, so the
platform device that you instantiate for the driver to bind to only
gets the one region through the .resource and .num_resources fields.
How is that going to work?
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-23 13:37 ` Thierry Reding
0 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-23 13:37 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > + /*
> > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > + * and 'APWM mode' to enable APWM output
> > > > > + */
> > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > >
> > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > clear in .config() is the better option.
> > >
> > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > in idle state).
> > >
> > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > should be set.
> > > The same is done in .config().
> >
> > My point was that if you do it in .enable(), then why do you still set
> > it in .config()? Since the sequence is typically .config() followed by
> > .enable(), setting the bit in both seems redundant. It should be enough
> > to load the registers during .enable(), right?
>
> Consider scenarios where .enable() can be called without calling .config().
> Example I just need to stop the pwm signal momentarily and re-enable.
> In this case, .config() need not be called. So, APWM mode bit needs to be
> set in .enable()
>
> Now, considering .config() followed by .enable().
> Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> mode bit to be set.
>
> The same can be done in .enable() also. However, we again need to pass
> the pwm parameters (period & duty cycle values) to the .enable().
> We don't want to duplicate this parameter passing.
> To avoid this we enable the APWM mode bit in both .config() & .enable().
That's weird. I assumed that the values written to the shadow registers
would automatically be copied to the active registers on each new PWM
period. If I understand correctly what you're saying, however, the eCAP
requires the APWM bit to be set in order to write the shadow registers
at all.
> > > > > + pc->chip.dev = &pdev->dev;
> > > > > + pc->chip.ops = &ecap_pwm_ops;
> > > > > + pc->chip.base = -1;
> > > > > + pc->chip.npwm = 1;
> > > >
> > > > The cover letter mentions that the AM335x has 3 instances of the ECAP.
> > > > Is there any chance that they can be unified in one driver instance
> > > > (i.e. .npwm = 3?).
> > >
> > > No. All instances have separate resources (clocks, memory ..).
> > >
> > > >
> > > > > +
> > > > > + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecap_reg");
> > > > > + if (r == NULL) {
> > > >
> > > > You use "if (!ptr)" everywhere else, maybe you should make this
> > > > consistent?
> > > Ok
> > > > Also the platform_get_resource_byname() is really only
> > > > useful for devices that have several register regions associated with
> > > > them. I don't know your hardware in detail, but I doubt that a PWM
> > > > device has more than one register region.
> > >
> > > In fact PWM Module has 4 different register space (eCAP, eHRPWM, eQEP &
> > > Common Config space). So I need to use platform_get_resource_byname()
> >
> > Above you say that all instances have separate resources, so how come
> > you need to specify 4 register spaces? The eCAP registers should clearly
> > be passed to the eCAP device, while the eHRPWM registers should go to
> > the eHRPWM device.
> >
> > My point is that if you need to refer to the register region by name,
> > then the driver should clearly be using more than a single region.
> > Neither the eCAP nor eHRPWM do that.
>
> On AM335x SoC, this common config space is shared by the other 3
> modules (eCAP, eHRPWM, eQEP).
> However, on Davinci platform don't have any common config space.
>
> So from driver reusability view, usage of platform_get_resource_byname()
> is better choice.
I don't quite see how you would be able to reuse the driver in that
case. The driver that you posted uses only one memory region, so the
platform device that you instantiate for the driver to bind to only
gets the one region through the .resource and .num_resources fields.
How is that going to work?
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120723/62e6b9c8/attachment.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* RE: [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-23 13:37 ` Thierry Reding
@ 2012-07-24 7:52 ` Philip, Avinash
-1 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-24 7:52 UTC (permalink / raw)
To: Thierry Reding; +Cc: linux-omap, linux-arm-kernel
On Mon, Jul 23, 2012 at 19:07:04, Thierry Reding wrote:
> On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> > On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > > + /*
> > > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > > + * and 'APWM mode' to enable APWM output
> > > > > > + */
> > > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > > >
> > > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > > clear in .config() is the better option.
> > > >
> > > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > > in idle state).
> > > >
> > > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > > should be set.
> > > > The same is done in .config().
> > >
> > > My point was that if you do it in .enable(), then why do you still set
> > > it in .config()? Since the sequence is typically .config() followed by
> > > .enable(), setting the bit in both seems redundant. It should be enough
> > > to load the registers during .enable(), right?
> >
> > Consider scenarios where .enable() can be called without calling .config().
> > Example I just need to stop the pwm signal momentarily and re-enable.
> > In this case, .config() need not be called. So, APWM mode bit needs to be
> > set in .enable()
> >
> > Now, considering .config() followed by .enable().
> > Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> > Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> > mode bit to be set.
> >
> > The same can be done in .enable() also. However, we again need to pass
> > the pwm parameters (period & duty cycle values) to the .enable().
> > We don't want to duplicate this parameter passing.
> > To avoid this we enable the APWM mode bit in both .config() & .enable().
>
> That's weird. I assumed that the values written to the shadow registers
> would automatically be copied to the active registers on each new PWM
> period.
This is correct in case if PWM is running.
> If I understand correctly what you're saying, however, the eCAP
> requires the APWM bit to be set in order to write the shadow registers
> at all.
In APWM mode, writing to CAP1/CAP2 (active registers) will also write the
same value to the corresponding CAP3/CAP4 (shadow registers). This emulates
immediate mode.
Writing directly to the shadow registers CAP3/CAP4 will invoke the
shadow mode.
If PWM is not running, cycle & duty values written to active registers, not
to shadow registers. To copy these value to shadow registers APWM mode to be
set. This way reloading from shadow registers can be ensured on next PWM
period/cycle.
If PWM is running, cycle & duty values written to shadow registers. So that
it won't disturb current PWM period. On the next PWM period, new values will
be reloaded from shadow registers to active registers.
So APWM mode to be set to copy shadow registers from active registers
(immediate mode).
>
[snip]
> > > My point is that if you need to refer to the register region by name,
> > > then the driver should clearly be using more than a single region.
> > > Neither the eCAP nor eHRPWM do that.
> >
> > On AM335x SoC, this common config space is shared by the other 3
> > modules (eCAP, eHRPWM, eQEP).
> > However, on Davinci platform don't have any common config space.
> >
> > So from driver reusability view, usage of platform_get_resource_byname()
> > is better choice.
>
> I don't quite see how you would be able to reuse the driver in that
> case. The driver that you posted uses only one memory region, so the
> platform device that you instantiate for the driver to bind to only
> gets the one region through the .resource and .num_resources fields.
>
> How is that going to work?
I understand now. I will use platform_get_resource().
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-24 7:52 ` Philip, Avinash
0 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-24 7:52 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jul 23, 2012 at 19:07:04, Thierry Reding wrote:
> On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> > On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > > + /*
> > > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > > + * and 'APWM mode' to enable APWM output
> > > > > > + */
> > > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > > >
> > > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > > clear in .config() is the better option.
> > > >
> > > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > > in idle state).
> > > >
> > > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > > should be set.
> > > > The same is done in .config().
> > >
> > > My point was that if you do it in .enable(), then why do you still set
> > > it in .config()? Since the sequence is typically .config() followed by
> > > .enable(), setting the bit in both seems redundant. It should be enough
> > > to load the registers during .enable(), right?
> >
> > Consider scenarios where .enable() can be called without calling .config().
> > Example I just need to stop the pwm signal momentarily and re-enable.
> > In this case, .config() need not be called. So, APWM mode bit needs to be
> > set in .enable()
> >
> > Now, considering .config() followed by .enable().
> > Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> > Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> > mode bit to be set.
> >
> > The same can be done in .enable() also. However, we again need to pass
> > the pwm parameters (period & duty cycle values) to the .enable().
> > We don't want to duplicate this parameter passing.
> > To avoid this we enable the APWM mode bit in both .config() & .enable().
>
> That's weird. I assumed that the values written to the shadow registers
> would automatically be copied to the active registers on each new PWM
> period.
This is correct in case if PWM is running.
> If I understand correctly what you're saying, however, the eCAP
> requires the APWM bit to be set in order to write the shadow registers
> at all.
In APWM mode, writing to CAP1/CAP2 (active registers) will also write the
same value to the corresponding CAP3/CAP4 (shadow registers). This emulates
immediate mode.
Writing directly to the shadow registers CAP3/CAP4 will invoke the
shadow mode.
If PWM is not running, cycle & duty values written to active registers, not
to shadow registers. To copy these value to shadow registers APWM mode to be
set. This way reloading from shadow registers can be ensured on next PWM
period/cycle.
If PWM is running, cycle & duty values written to shadow registers. So that
it won't disturb current PWM period. On the next PWM period, new values will
be reloaded from shadow registers to active registers.
So APWM mode to be set to copy shadow registers from active registers
(immediate mode).
>
[snip]
> > > My point is that if you need to refer to the register region by name,
> > > then the driver should clearly be using more than a single region.
> > > Neither the eCAP nor eHRPWM do that.
> >
> > On AM335x SoC, this common config space is shared by the other 3
> > modules (eCAP, eHRPWM, eQEP).
> > However, on Davinci platform don't have any common config space.
> >
> > So from driver reusability view, usage of platform_get_resource_byname()
> > is better choice.
>
> I don't quite see how you would be able to reuse the driver in that
> case. The driver that you posted uses only one memory region, so the
> platform device that you instantiate for the driver to bind to only
> gets the one region through the .resource and .num_resources fields.
>
> How is that going to work?
I understand now. I will use platform_get_resource().
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-24 7:52 ` Philip, Avinash
@ 2012-07-24 8:07 ` Thierry Reding
-1 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-24 8:07 UTC (permalink / raw)
To: Philip, Avinash; +Cc: linux-omap, linux-arm-kernel
[-- Attachment #1: Type: text/plain, Size: 4249 bytes --]
On Tue, Jul 24, 2012 at 07:52:06AM +0000, Philip, Avinash wrote:
> On Mon, Jul 23, 2012 at 19:07:04, Thierry Reding wrote:
> > On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> > > On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > > > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > > > + /*
> > > > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > > > + * and 'APWM mode' to enable APWM output
> > > > > > > + */
> > > > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > > > >
> > > > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > > > clear in .config() is the better option.
> > > > >
> > > > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > > > in idle state).
> > > > >
> > > > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > > > should be set.
> > > > > The same is done in .config().
> > > >
> > > > My point was that if you do it in .enable(), then why do you still set
> > > > it in .config()? Since the sequence is typically .config() followed by
> > > > .enable(), setting the bit in both seems redundant. It should be enough
> > > > to load the registers during .enable(), right?
> > >
> > > Consider scenarios where .enable() can be called without calling .config().
> > > Example I just need to stop the pwm signal momentarily and re-enable.
> > > In this case, .config() need not be called. So, APWM mode bit needs to be
> > > set in .enable()
> > >
> > > Now, considering .config() followed by .enable().
> > > Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> > > Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> > > mode bit to be set.
> > >
> > > The same can be done in .enable() also. However, we again need to pass
> > > the pwm parameters (period & duty cycle values) to the .enable().
> > > We don't want to duplicate this parameter passing.
> > > To avoid this we enable the APWM mode bit in both .config() & .enable().
> >
> > That's weird. I assumed that the values written to the shadow registers
> > would automatically be copied to the active registers on each new PWM
> > period.
>
> This is correct in case if PWM is running.
>
> > If I understand correctly what you're saying, however, the eCAP
> > requires the APWM bit to be set in order to write the shadow registers
> > at all.
>
> In APWM mode, writing to CAP1/CAP2 (active registers) will also write the
> same value to the corresponding CAP3/CAP4 (shadow registers). This emulates
> immediate mode.
>
> Writing directly to the shadow registers CAP3/CAP4 will invoke the
> shadow mode.
>
> If PWM is not running, cycle & duty values written to active registers, not
> to shadow registers. To copy these value to shadow registers APWM mode to be
> set. This way reloading from shadow registers can be ensured on next PWM
> period/cycle.
I think this is the part I don't understand. If you wrote cycle and duty
values to the active registers already, then the shadow registers should
be ignored by the hardware. There should be no need to reload the active
registers.
> If PWM is running, cycle & duty values written to shadow registers. So that
> it won't disturb current PWM period. On the next PWM period, new values will
> be reloaded from shadow registers to active registers.
>
> So APWM mode to be set to copy shadow registers from active registers
> (immediate mode).
I realize that I'm just talking from a general understanding of shadow
registers and maybe you're hardware doesn't work quite that way. If this
is really the only way that you can make the hardware work then I will
no longer object.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-24 8:07 ` Thierry Reding
0 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-24 8:07 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jul 24, 2012 at 07:52:06AM +0000, Philip, Avinash wrote:
> On Mon, Jul 23, 2012 at 19:07:04, Thierry Reding wrote:
> > On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> > > On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > > > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > > > + /*
> > > > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > > > + * and 'APWM mode' to enable APWM output
> > > > > > > + */
> > > > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > > > >
> > > > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > > > clear in .config() is the better option.
> > > > >
> > > > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > > > in idle state).
> > > > >
> > > > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > > > should be set.
> > > > > The same is done in .config().
> > > >
> > > > My point was that if you do it in .enable(), then why do you still set
> > > > it in .config()? Since the sequence is typically .config() followed by
> > > > .enable(), setting the bit in both seems redundant. It should be enough
> > > > to load the registers during .enable(), right?
> > >
> > > Consider scenarios where .enable() can be called without calling .config().
> > > Example I just need to stop the pwm signal momentarily and re-enable.
> > > In this case, .config() need not be called. So, APWM mode bit needs to be
> > > set in .enable()
> > >
> > > Now, considering .config() followed by .enable().
> > > Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> > > Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> > > mode bit to be set.
> > >
> > > The same can be done in .enable() also. However, we again need to pass
> > > the pwm parameters (period & duty cycle values) to the .enable().
> > > We don't want to duplicate this parameter passing.
> > > To avoid this we enable the APWM mode bit in both .config() & .enable().
> >
> > That's weird. I assumed that the values written to the shadow registers
> > would automatically be copied to the active registers on each new PWM
> > period.
>
> This is correct in case if PWM is running.
>
> > If I understand correctly what you're saying, however, the eCAP
> > requires the APWM bit to be set in order to write the shadow registers
> > at all.
>
> In APWM mode, writing to CAP1/CAP2 (active registers) will also write the
> same value to the corresponding CAP3/CAP4 (shadow registers). This emulates
> immediate mode.
>
> Writing directly to the shadow registers CAP3/CAP4 will invoke the
> shadow mode.
>
> If PWM is not running, cycle & duty values written to active registers, not
> to shadow registers. To copy these value to shadow registers APWM mode to be
> set. This way reloading from shadow registers can be ensured on next PWM
> period/cycle.
I think this is the part I don't understand. If you wrote cycle and duty
values to the active registers already, then the shadow registers should
be ignored by the hardware. There should be no need to reload the active
registers.
> If PWM is running, cycle & duty values written to shadow registers. So that
> it won't disturb current PWM period. On the next PWM period, new values will
> be reloaded from shadow registers to active registers.
>
> So APWM mode to be set to copy shadow registers from active registers
> (immediate mode).
I realize that I'm just talking from a general understanding of shadow
registers and maybe you're hardware doesn't work quite that way. If this
is really the only way that you can make the hardware work then I will
no longer object.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120724/41985e23/attachment-0001.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* RE: [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-24 8:07 ` Thierry Reding
@ 2012-07-24 8:36 ` Philip, Avinash
-1 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-24 8:36 UTC (permalink / raw)
To: Thierry Reding; +Cc: linux-omap, linux-arm-kernel
On Tue, Jul 24, 2012 at 13:37:24, Thierry Reding wrote:
> On Tue, Jul 24, 2012 at 07:52:06AM +0000, Philip, Avinash wrote:
> > On Mon, Jul 23, 2012 at 19:07:04, Thierry Reding wrote:
> > > On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> > > > On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > > > > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > > > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > > > > + /*
> > > > > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > > > > + * and 'APWM mode' to enable APWM output
> > > > > > > > + */
> > > > > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > > > > >
> > > > > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > > > > clear in .config() is the better option.
> > > > > >
> > > > > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > > > > in idle state).
> > > > > >
> > > > > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > > > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > > > > should be set.
> > > > > > The same is done in .config().
> > > > >
> > > > > My point was that if you do it in .enable(), then why do you still set
> > > > > it in .config()? Since the sequence is typically .config() followed by
> > > > > .enable(), setting the bit in both seems redundant. It should be enough
> > > > > to load the registers during .enable(), right?
> > > >
> > > > Consider scenarios where .enable() can be called without calling .config().
> > > > Example I just need to stop the pwm signal momentarily and re-enable.
> > > > In this case, .config() need not be called. So, APWM mode bit needs to be
> > > > set in .enable()
> > > >
> > > > Now, considering .config() followed by .enable().
> > > > Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> > > > Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> > > > mode bit to be set.
> > > >
> > > > The same can be done in .enable() also. However, we again need to pass
> > > > the pwm parameters (period & duty cycle values) to the .enable().
> > > > We don't want to duplicate this parameter passing.
> > > > To avoid this we enable the APWM mode bit in both .config() & .enable().
> > >
> > > That's weird. I assumed that the values written to the shadow registers
> > > would automatically be copied to the active registers on each new PWM
> > > period.
> >
> > This is correct in case if PWM is running.
> >
> > > If I understand correctly what you're saying, however, the eCAP
> > > requires the APWM bit to be set in order to write the shadow registers
> > > at all.
> >
> > In APWM mode, writing to CAP1/CAP2 (active registers) will also write the
> > same value to the corresponding CAP3/CAP4 (shadow registers). This emulates
> > immediate mode.
> >
> > Writing directly to the shadow registers CAP3/CAP4 will invoke the
> > shadow mode.
> >
> > If PWM is not running, cycle & duty values written to active registers, not
> > to shadow registers. To copy these value to shadow registers APWM mode to be
> > set. This way reloading from shadow registers can be ensured on next PWM
> > period/cycle.
>
> I think this is the part I don't understand. If you wrote cycle and duty
> values to the active registers already, then the shadow registers should
> be ignored by the hardware. There should be no need to reload the active
> registers.
ECAP PWM hardware always loads active registers from shadow registers at
counter = period value (starting of next period). So on every next cycle
active registers being updated from shadow registers. So shadow registers
acting as a backup.
>
> > If PWM is running, cycle & duty values written to shadow registers. So that
> > it won't disturb current PWM period. On the next PWM period, new values will
> > be reloaded from shadow registers to active registers.
> >
> > So APWM mode to be set to copy shadow registers from active registers
> > (immediate mode).
>
> I realize that I'm just talking from a general understanding of shadow
> registers and maybe you're hardware doesn't work quite that way. If this
> is really the only way that you can make the hardware work then I will
> no longer object.
Ok.
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-24 8:36 ` Philip, Avinash
0 siblings, 0 replies; 28+ messages in thread
From: Philip, Avinash @ 2012-07-24 8:36 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jul 24, 2012 at 13:37:24, Thierry Reding wrote:
> On Tue, Jul 24, 2012 at 07:52:06AM +0000, Philip, Avinash wrote:
> > On Mon, Jul 23, 2012 at 19:07:04, Thierry Reding wrote:
> > > On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> > > > On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > > > > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > > > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > > > > + /*
> > > > > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > > > > + * and 'APWM mode' to enable APWM output
> > > > > > > > + */
> > > > > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > > > > >
> > > > > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > > > > clear in .config() is the better option.
> > > > > >
> > > > > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > > > > in idle state).
> > > > > >
> > > > > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > > > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > > > > should be set.
> > > > > > The same is done in .config().
> > > > >
> > > > > My point was that if you do it in .enable(), then why do you still set
> > > > > it in .config()? Since the sequence is typically .config() followed by
> > > > > .enable(), setting the bit in both seems redundant. It should be enough
> > > > > to load the registers during .enable(), right?
> > > >
> > > > Consider scenarios where .enable() can be called without calling .config().
> > > > Example I just need to stop the pwm signal momentarily and re-enable.
> > > > In this case, .config() need not be called. So, APWM mode bit needs to be
> > > > set in .enable()
> > > >
> > > > Now, considering .config() followed by .enable().
> > > > Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> > > > Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> > > > mode bit to be set.
> > > >
> > > > The same can be done in .enable() also. However, we again need to pass
> > > > the pwm parameters (period & duty cycle values) to the .enable().
> > > > We don't want to duplicate this parameter passing.
> > > > To avoid this we enable the APWM mode bit in both .config() & .enable().
> > >
> > > That's weird. I assumed that the values written to the shadow registers
> > > would automatically be copied to the active registers on each new PWM
> > > period.
> >
> > This is correct in case if PWM is running.
> >
> > > If I understand correctly what you're saying, however, the eCAP
> > > requires the APWM bit to be set in order to write the shadow registers
> > > at all.
> >
> > In APWM mode, writing to CAP1/CAP2 (active registers) will also write the
> > same value to the corresponding CAP3/CAP4 (shadow registers). This emulates
> > immediate mode.
> >
> > Writing directly to the shadow registers CAP3/CAP4 will invoke the
> > shadow mode.
> >
> > If PWM is not running, cycle & duty values written to active registers, not
> > to shadow registers. To copy these value to shadow registers APWM mode to be
> > set. This way reloading from shadow registers can be ensured on next PWM
> > period/cycle.
>
> I think this is the part I don't understand. If you wrote cycle and duty
> values to the active registers already, then the shadow registers should
> be ignored by the hardware. There should be no need to reload the active
> registers.
ECAP PWM hardware always loads active registers from shadow registers at
counter = period value (starting of next period). So on every next cycle
active registers being updated from shadow registers. So shadow registers
acting as a backup.
>
> > If PWM is running, cycle & duty values written to shadow registers. So that
> > it won't disturb current PWM period. On the next PWM period, new values will
> > be reloaded from shadow registers to active registers.
> >
> > So APWM mode to be set to copy shadow registers from active registers
> > (immediate mode).
>
> I realize that I'm just talking from a general understanding of shadow
> registers and maybe you're hardware doesn't work quite that way. If this
> is really the only way that you can make the hardware work then I will
> no longer object.
Ok.
Thanks
Avinash
>
> Thierry
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
2012-07-24 8:36 ` Philip, Avinash
@ 2012-07-24 8:48 ` Thierry Reding
-1 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-24 8:48 UTC (permalink / raw)
To: Philip, Avinash; +Cc: linux-omap, linux-arm-kernel
[-- Attachment #1: Type: text/plain, Size: 4414 bytes --]
On Tue, Jul 24, 2012 at 08:36:08AM +0000, Philip, Avinash wrote:
> On Tue, Jul 24, 2012 at 13:37:24, Thierry Reding wrote:
> > On Tue, Jul 24, 2012 at 07:52:06AM +0000, Philip, Avinash wrote:
> > > On Mon, Jul 23, 2012 at 19:07:04, Thierry Reding wrote:
> > > > On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> > > > > On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > > > > > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > > > > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > > > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > > > > > + /*
> > > > > > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > > > > > + * and 'APWM mode' to enable APWM output
> > > > > > > > > + */
> > > > > > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > > > > > >
> > > > > > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > > > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > > > > > clear in .config() is the better option.
> > > > > > >
> > > > > > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > > > > > in idle state).
> > > > > > >
> > > > > > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > > > > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > > > > > should be set.
> > > > > > > The same is done in .config().
> > > > > >
> > > > > > My point was that if you do it in .enable(), then why do you still set
> > > > > > it in .config()? Since the sequence is typically .config() followed by
> > > > > > .enable(), setting the bit in both seems redundant. It should be enough
> > > > > > to load the registers during .enable(), right?
> > > > >
> > > > > Consider scenarios where .enable() can be called without calling .config().
> > > > > Example I just need to stop the pwm signal momentarily and re-enable.
> > > > > In this case, .config() need not be called. So, APWM mode bit needs to be
> > > > > set in .enable()
> > > > >
> > > > > Now, considering .config() followed by .enable().
> > > > > Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> > > > > Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> > > > > mode bit to be set.
> > > > >
> > > > > The same can be done in .enable() also. However, we again need to pass
> > > > > the pwm parameters (period & duty cycle values) to the .enable().
> > > > > We don't want to duplicate this parameter passing.
> > > > > To avoid this we enable the APWM mode bit in both .config() & .enable().
> > > >
> > > > That's weird. I assumed that the values written to the shadow registers
> > > > would automatically be copied to the active registers on each new PWM
> > > > period.
> > >
> > > This is correct in case if PWM is running.
> > >
> > > > If I understand correctly what you're saying, however, the eCAP
> > > > requires the APWM bit to be set in order to write the shadow registers
> > > > at all.
> > >
> > > In APWM mode, writing to CAP1/CAP2 (active registers) will also write the
> > > same value to the corresponding CAP3/CAP4 (shadow registers). This emulates
> > > immediate mode.
> > >
> > > Writing directly to the shadow registers CAP3/CAP4 will invoke the
> > > shadow mode.
> > >
> > > If PWM is not running, cycle & duty values written to active registers, not
> > > to shadow registers. To copy these value to shadow registers APWM mode to be
> > > set. This way reloading from shadow registers can be ensured on next PWM
> > > period/cycle.
> >
> > I think this is the part I don't understand. If you wrote cycle and duty
> > values to the active registers already, then the shadow registers should
> > be ignored by the hardware. There should be no need to reload the active
> > registers.
>
> ECAP PWM hardware always loads active registers from shadow registers at
> counter = period value (starting of next period). So on every next cycle
> active registers being updated from shadow registers. So shadow registers
> acting as a backup.
Okay, in that case I don't see any other option.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM.
@ 2012-07-24 8:48 ` Thierry Reding
0 siblings, 0 replies; 28+ messages in thread
From: Thierry Reding @ 2012-07-24 8:48 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jul 24, 2012 at 08:36:08AM +0000, Philip, Avinash wrote:
> On Tue, Jul 24, 2012 at 13:37:24, Thierry Reding wrote:
> > On Tue, Jul 24, 2012 at 07:52:06AM +0000, Philip, Avinash wrote:
> > > On Mon, Jul 23, 2012 at 19:07:04, Thierry Reding wrote:
> > > > On Mon, Jul 23, 2012 at 12:44:38PM +0000, Philip, Avinash wrote:
> > > > > On Mon, Jul 23, 2012 at 14:52:04, Thierry Reding wrote:
> > > > > > On Mon, Jul 23, 2012 at 09:10:09AM +0000, Philip, Avinash wrote:
> > > > > > > On Mon, Jul 23, 2012 at 12:22:35, Thierry Reding wrote:
> > > > > > > > On Fri, Jul 13, 2012 at 03:05:01PM +0530, Philip, Avinash wrote:
> > > > > > > > > + /*
> > > > > > > > > + * Enable 'Free run Time stamp counter mode' to start counter
> > > > > > > > > + * and 'APWM mode' to enable APWM output
> > > > > > > > > + */
> > > > > > > > > + reg_val = readw(pc->mmio_base + ECCTL2);
> > > > > > > > > + reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
> > > > > > > >
> > > > > > > > You already set the APWM_MODE bit in .config(), why is it needed here
> > > > > > > > again? Seeing that .disable() clears the bit as well, perhaps leaving it
> > > > > > > > clear in .config() is the better option.
> > > > > > >
> > > > > > > Clearing APWM mode in .disable() ensures PWM low output (i.e., PWM pin = low
> > > > > > > in idle state).
> > > > > > >
> > > > > > > The Period & Compare registers are updated from Shadow registers (CAP1 & CAP2)
> > > > > > > During PWM configuration. To enable loading from Shadow registers, APWM mode
> > > > > > > should be set.
> > > > > > > The same is done in .config().
> > > > > >
> > > > > > My point was that if you do it in .enable(), then why do you still set
> > > > > > it in .config()? Since the sequence is typically .config() followed by
> > > > > > .enable(), setting the bit in both seems redundant. It should be enough
> > > > > > to load the registers during .enable(), right?
> > > > >
> > > > > Consider scenarios where .enable() can be called without calling .config().
> > > > > Example I just need to stop the pwm signal momentarily and re-enable.
> > > > > In this case, .config() need not be called. So, APWM mode bit needs to be
> > > > > set in .enable()
> > > > >
> > > > > Now, considering .config() followed by .enable().
> > > > > Currently in PWM driver, the CAP1 & CAP2 registers is copied to shadow
> > > > > Registers (CAP3 & CAP4) by hardwaare in .config(). This requires APWM
> > > > > mode bit to be set.
> > > > >
> > > > > The same can be done in .enable() also. However, we again need to pass
> > > > > the pwm parameters (period & duty cycle values) to the .enable().
> > > > > We don't want to duplicate this parameter passing.
> > > > > To avoid this we enable the APWM mode bit in both .config() & .enable().
> > > >
> > > > That's weird. I assumed that the values written to the shadow registers
> > > > would automatically be copied to the active registers on each new PWM
> > > > period.
> > >
> > > This is correct in case if PWM is running.
> > >
> > > > If I understand correctly what you're saying, however, the eCAP
> > > > requires the APWM bit to be set in order to write the shadow registers
> > > > at all.
> > >
> > > In APWM mode, writing to CAP1/CAP2 (active registers) will also write the
> > > same value to the corresponding CAP3/CAP4 (shadow registers). This emulates
> > > immediate mode.
> > >
> > > Writing directly to the shadow registers CAP3/CAP4 will invoke the
> > > shadow mode.
> > >
> > > If PWM is not running, cycle & duty values written to active registers, not
> > > to shadow registers. To copy these value to shadow registers APWM mode to be
> > > set. This way reloading from shadow registers can be ensured on next PWM
> > > period/cycle.
> >
> > I think this is the part I don't understand. If you wrote cycle and duty
> > values to the active registers already, then the shadow registers should
> > be ignored by the hardware. There should be no need to reload the active
> > registers.
>
> ECAP PWM hardware always loads active registers from shadow registers at
> counter = period value (starting of next period). So on every next cycle
> active registers being updated from shadow registers. So shadow registers
> acting as a backup.
Okay, in that case I don't see any other option.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120724/55639ece/attachment.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2012-07-24 8:48 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-07-13 9:35 [PATCH 0/2] PWM driver support for EHRPWM & ECAP Philip, Avinash
2012-07-13 9:35 ` Philip, Avinash
2012-07-13 9:35 ` [PATCH 1/2] PWM: ECAP: PWM driver support for ECAP APWM Philip, Avinash
2012-07-13 9:35 ` Philip, Avinash
2012-07-23 6:52 ` Thierry Reding
2012-07-23 6:52 ` Thierry Reding
2012-07-23 9:10 ` Philip, Avinash
2012-07-23 9:10 ` Philip, Avinash
2012-07-23 9:22 ` Thierry Reding
2012-07-23 9:22 ` Thierry Reding
2012-07-23 12:44 ` Philip, Avinash
2012-07-23 12:44 ` Philip, Avinash
2012-07-23 13:37 ` Thierry Reding
2012-07-23 13:37 ` Thierry Reding
2012-07-24 7:52 ` Philip, Avinash
2012-07-24 7:52 ` Philip, Avinash
2012-07-24 8:07 ` Thierry Reding
2012-07-24 8:07 ` Thierry Reding
2012-07-24 8:36 ` Philip, Avinash
2012-07-24 8:36 ` Philip, Avinash
2012-07-24 8:48 ` Thierry Reding
2012-07-24 8:48 ` Thierry Reding
2012-07-13 9:35 ` [PATCH 2/2] PWM: EHRPWM: PWM driver support for EHRPWM Philip, Avinash
2012-07-13 9:35 ` Philip, Avinash
2012-07-23 7:42 ` Thierry Reding
2012-07-23 7:42 ` Thierry Reding
2012-07-23 9:16 ` Philip, Avinash
2012-07-23 9:16 ` Philip, Avinash
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.