diff mbox

[1/2] PWM: ECAP: PWM driver support for ECAP APWM.

Message ID 1342172102-30363-2-git-send-email-avinashphilip@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

avinash philip July 13, 2012, 9:35 a.m. UTC
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(-)

Comments

Thierry Reding July 23, 2012, 6:52 a.m. UTC | #1
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
> 
>
avinash philip July 23, 2012, 9:10 a.m. UTC | #2
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
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Thierry Reding July 23, 2012, 9:22 a.m. UTC | #3
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
avinash philip July 23, 2012, 12:44 p.m. UTC | #4
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
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Thierry Reding July 23, 2012, 1:37 p.m. UTC | #5
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
avinash philip July 24, 2012, 7:52 a.m. UTC | #6
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
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Thierry Reding July 24, 2012, 8:07 a.m. UTC | #7
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
avinash philip July 24, 2012, 8:36 a.m. UTC | #8
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
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Thierry Reding July 24, 2012, 8:48 a.m. UTC | #9
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
diff mbox

Patch

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");