linux-sh.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] sh: added PWM driver for SH7723 using the TPU
@ 2010-02-03 13:05 Pietrek, Markus
  2010-02-03 14:35 ` AW: " Pietrek, Markus
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Pietrek, Markus @ 2010-02-03 13:05 UTC (permalink / raw)
  To: linux-sh

this PWM driver using the TPU can be used with drivers/video/backlight/pwm_bl.c to dim the backlight

Signed-off-by: Markus Pietrek <markus.pietrek@emtrion.de>
---
 arch/sh/Kconfig           |    3 +
 arch/sh/drivers/Kconfig   |    7 +
 arch/sh/drivers/Makefile  |    1 +
 arch/sh/drivers/pwm_tpu.c |  332 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 343 insertions(+), 0 deletions(-)
 create mode 100644 arch/sh/drivers/pwm_tpu.c

diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index bae5383..54ecbae 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -182,6 +182,9 @@ config DMA_COHERENT
 config DMA_NONCOHERENT
        def_bool !DMA_COHERENT

+config HAVE_PWM
+       bool
+
 source "init/Kconfig"

 source "kernel/Kconfig.freezer"
diff --git a/arch/sh/drivers/Kconfig b/arch/sh/drivers/Kconfig
index 420c6b2..83c8794 100644
--- a/arch/sh/drivers/Kconfig
+++ b/arch/sh/drivers/Kconfig
@@ -16,4 +16,11 @@ config PUSH_SWITCH
          This enables support for the push switch framework, a simple
          framework that allows for sysfs driven switch status reporting.

+config SH_PWM_TPU
+       bool "PWM with Timer Pulse Unit (TPU) Driver"
+       depends on CPU_SUBTYPE_SH7723
+       select HAVE_PWM
+        help
+          Provides a PWM driver using the Timer Pulse Unit of the SH7723
+
 endmenu
diff --git a/arch/sh/drivers/Makefile b/arch/sh/drivers/Makefile
index e13f06b..3dcaf83 100644
--- a/arch/sh/drivers/Makefile
+++ b/arch/sh/drivers/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PCI)               += pci/
 obj-$(CONFIG_SUPERHYWAY)       += superhyway/
 obj-$(CONFIG_PUSH_SWITCH)      += push-switch.o
 obj-$(CONFIG_HEARTBEAT)                += heartbeat.o
+obj-$(CONFIG_SH_PWM_TPU)        += tpu.o
diff --git a/arch/sh/drivers/pwm_tpu.c b/arch/sh/drivers/pwm_tpu.c
new file mode 100644
index 0000000..d79733c
--- /dev/null
+++ b/arch/sh/drivers/pwm_tpu.c
@@ -0,0 +1,332 @@
+/*
+ * pwm_tpu.c
+ *
+ * Copyright (c) 2010 by emtrion GmbH
+ *
+ * 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.
+ *
+ * Author:     Markus Pietrek
+ * Description: Provides PWM functionality with the TPU.
+ *             TPU outputs 1 in disabled state and 0 in active state.
+ *
+ **/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#ifdef CONFIG_CPU_SUBTYPE_SH7723
+# include <cpu/sh7723.h>
+#endif
+
+/* TPU register definitions */
+#define TSTR           0x00
+
+/* TPU channel specific registers */
+#define TPUC_BASE      0x10
+#define TPUC_OFFSET    0x40
+
+#define TCR            0x00
+#define TMDR           0x04
+#define TIOR           0x08
+#define TIER           0x0C
+#define TSR            0x10
+#define TCNT           0x14
+#define TGRA           0x18
+#define TGRB           0x1C
+#define TGRC           0x20
+#define TGRD           0x24
+
+/* TPU register bits */
+#define TCR_CCLR_TGRB_CLEARS   0x40
+#define TCR_CKEG_RISING                0x00
+#define TCR_TPSC_64            0x03
+
+#define TIOR_OUTPUT_ALWAYS_0                   0x00
+#define TIOR_OUTPUT_ALWAYS_1                   0x04
+#define TIOR_INIT_0_OUTPUT_1_ON_MATCH_TGRA     0x02
+#define TIOR_INIT_1_OUTPUT_0_ON_MATCH_TGRA     0x05
+
+#define TMDR_PWM       0x02
+
+#define CHANNELS 4
+
+struct pwm_device {
+       const char *label;
+       int pwm_id;
+       int active;             /* for counting whether the clock on the TPU needs to be enabled */
+};
+
+static struct pwm_tpu_priv {
+       struct device *dev;
+       void __iomem *base;
+       struct clk *clk;
+       struct clk *bclk;
+       struct mutex mutex;
+
+       struct pwm_device devices[CHANNELS];
+       int clk_enabled;
+} *priv;
+
+static const int gpios[CHANNELS] = {
+#ifdef CONFIG_CPU_SUBTYPE_SH7723
+       GPIO_PTG0,
+       GPIO_PTG1,
+       GPIO_PTG2,
+       GPIO_PTG3,
+#else
+# error no GPIO configuration for cpu
+#endif
+};
+
+static inline void tpuc_write(const struct pwm_tpu_priv *priv, int ch, u16 val, int reg)
+{
+       iowrite16(val, priv->base + TPUC_BASE + (ch*TPUC_OFFSET) + reg);
+}
+
+static inline u16 tpuc_read(const struct pwm_tpu_priv *priv, int ch, int reg)
+{
+       return ioread16(priv->base + TPUC_BASE + (ch*TPUC_OFFSET) + reg);
+}
+
+/* exported pwm functions */
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+       struct pwm_device *pwm = NULL;
+       int error;
+
+       mutex_lock(&priv->mutex);
+
+       if (!label) {
+               dev_err(priv->dev, "need a name for pwm %i\n", pwm_id);
+               pwm = ERR_PTR(-EINVAL);
+               goto out;
+       }
+
+       if (pwm_id >= ARRAY_SIZE(priv->devices) || priv->devices[pwm_id].label) {
+               dev_err(priv->dev, "TPU %i already in use %s\n", pwm_id, priv->devices[pwm_id].label);
+               pwm = ERR_PTR(-EBUSY);
+               goto out;
+       }
+       pwm = &priv->devices[pwm_id];
+
+       error = gpio_request(gpios[pwm_id], label);
+       if (error) {
+               dev_err(priv->dev, "GPIO %i already reserved.\n", gpios[pwm_id] );
+               pwm = ERR_PTR(error);
+               goto out;
+       }
+       pwm->label  = label;
+       pwm->pwm_id = pwm_id;
+
+       dev_info(priv->dev, "TPU %i registered for %s\n", pwm_id, label);
+
+out:
+       mutex_unlock(&priv->mutex);
+       return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+       mutex_lock(&priv->mutex);
+       gpio_free(gpios[pwm->pwm_id]);
+       pwm->label = NULL;
+       mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL(pwm_free);
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+       int period_hz;
+       int tgra;
+       int tgrb;
+
+       BUG_ON((period_ns <= 0) || (duty_ns > period_ns));
+
+       mutex_lock(&priv->mutex);
+
+       /* enable clock for TPU when it was off before  */
+       if (!priv->clk_enabled) {
+               clk_enable(priv->clk);
+               priv->clk_enabled = 1;
+       }
+       pwm->active = 1;
+
+       /* calculate TGRA and TGRB settings based on the duty/period durations */
+       period_hz = 1000000000 / period_ns;
+       tgrb = ((clk_get_rate(priv->bclk)/64) / period_hz);
+
+       if (duty_ns >= period_ns)
+               tgra = 0;       /* 100% duty */
+       else if (duty_ns <= 0)
+               tgra = tgrb + 1; /* off */
+       else
+               tgra = tgrb - (tgrb * (duty_ns/1000))/(period_ns/1000);
+
+       /* configure TPU */
+       tpuc_write(priv, pwm->pwm_id,
+                  TCR_CCLR_TGRB_CLEARS |
+                  TCR_CKEG_RISING      |
+                  TCR_TPSC_64,
+                  TCR);
+       tpuc_write(priv, pwm->pwm_id,
+                  TIOR_INIT_1_OUTPUT_0_ON_MATCH_TGRA,
+                  TIOR);
+       tpuc_write(priv, pwm->pwm_id, tgra, TGRA);
+       tpuc_write(priv, pwm->pwm_id, tgrb, TGRB);
+       tpuc_write(priv, pwm->pwm_id, TMDR_PWM, TMDR);
+
+       mutex_unlock(&priv->mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+       /* pwm_config is called before enable, so we don't need to enable priv->clk here */
+       mutex_lock(&priv->mutex);
+       iowrite16(ioread16(priv->base+TSTR) | (1<<pwm->pwm_id), priv->base+TSTR);
+       mutex_unlock(&priv->mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+       int devices_active = 0;
+       int i;
+
+       mutex_lock(&priv->mutex);
+
+       /* set output to off state */
+       tpuc_write(priv, pwm->pwm_id,
+                  TIOR_OUTPUT_ALWAYS_1,
+                  TIOR);
+       iowrite16(ioread16(priv->base+TSTR) & ~(1<<pwm->pwm_id), priv->base+TSTR);
+
+       /* disable power when TPU is no longer in use */
+       pwm->active = 0;
+       for (i=0; i < ARRAY_SIZE(priv->devices); i++) {
+               if (priv->devices[i].active) {
+                       devices_active = 1;
+                       break;
+               }
+       }
+
+       if (!devices_active) {
+               clk_disable(priv->clk);
+               priv->clk_enabled = 0;
+       }
+
+       mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL(pwm_disable);
+
+/* driver registering functions */
+
+static int __devinit pwm_tpu_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int error;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(&pdev->dev, "cannot get memory\n");
+               error = -ENOMEM;
+               goto error;
+       }
+       priv->dev = &pdev->dev;
+       platform_set_drvdata(pdev, priv);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "cannot get platform resources\n");
+               error = -EINVAL;
+               goto error_map;
+       }
+
+       priv->base = ioremap(res->start, (res->end-res->start)+1);
+       if (!priv->base) {
+               dev_err(&pdev->dev, "cannot get io memory\n");
+               error = -ENOMEM;
+               goto error_map;
+       }
+
+       priv->clk = clk_get(&pdev->dev, "tpu0");
+       if (IS_ERR(priv->clk)) {
+               dev_err(&pdev->dev, "cannot get clock");
+               goto error_clock;
+       }
+
+       priv->bclk = clk_get(&pdev->dev, "bus_clk");
+       if (IS_ERR(priv->bclk)) {
+               dev_err(&pdev->dev, "cannot get bus clock");
+               goto error_bclock;
+       }
+
+       mutex_init(&priv->mutex);
+
+       dev_info(&pdev->dev, "initialized\n");
+       return 0;
+
+error_bclock:
+       clk_put(priv->bclk);
+
+error_clock:
+       iounmap(priv->base);
+
+error_map:
+       kfree(priv);
+
+error:
+       return error;
+}
+
+static int __devexit pwm_tpu_remove(struct platform_device *pdev)
+{
+       clk_put(priv->bclk);
+       clk_put(priv->clk);
+       iounmap(priv->base);
+       kfree(priv);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver pwm_tpu_driver = {
+       .probe          = pwm_tpu_probe,
+       .remove         = pwm_tpu_remove,
+       .driver         = {
+               .name   = "pwm_tpu",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init pwm_tpu_init(void)
+{
+       return platform_driver_register(&pwm_tpu_driver);
+}
+
+static void __exit pwm_tpu_exit(void)
+{
+       platform_driver_unregister(&pwm_tpu_driver);
+}
+
+MODULE_AUTHOR("Markus Pietrek");
+MODULE_DESCRIPTION("PWM/Timer Pulse Unit TPU control");
+MODULE_LICENSE("GPL");
+
+arch_initcall(pwm_tpu_init);
+module_exit(pwm_tpu_exit);
--
1.5.4.3

_____________________________________

Amtsgericht Mannheim
HRB 110 300
Gesch?ftsf?hrer: Dieter Baur, Ramona Maurer
_____________________________________

Important Note:
- This e-mail may contain trade secrets or privileged, undisclosed or otherwise confidential information.
- If you have received this e-mail in error, you are hereby notified that any review, copying or distribution of it is strictly prohibited.
- Please inform us immediately and destroy the original transmittal.

Thank you for your cooperation.

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* AW: [PATCH] sh: added PWM driver for SH7723 using the TPU
  2010-02-03 13:05 [PATCH] sh: added PWM driver for SH7723 using the TPU Pietrek, Markus
@ 2010-02-03 14:35 ` Pietrek, Markus
  2010-02-03 23:39 ` Paul Mundt
  2010-02-04  8:11 ` AW: " Pietrek, Markus
  2 siblings, 0 replies; 4+ messages in thread
From: Pietrek, Markus @ 2010-02-03 14:35 UTC (permalink / raw)
  To: linux-sh

Please use this patch


Signed-off-by: Markus Pietrek <markus.pietrek@emtrion.de>
---
 arch/sh/Kconfig           |    3 +
 arch/sh/drivers/Kconfig   |    7 +
 arch/sh/drivers/Makefile  |    1 +
 arch/sh/drivers/pwm_tpu.c |  332 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 343 insertions(+), 0 deletions(-)
 create mode 100644 arch/sh/drivers/pwm_tpu.c

diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index bae5383..54ecbae 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -182,6 +182,9 @@ config DMA_COHERENT
 config DMA_NONCOHERENT
        def_bool !DMA_COHERENT

+config HAVE_PWM
+       bool
+
 source "init/Kconfig"

 source "kernel/Kconfig.freezer"
diff --git a/arch/sh/drivers/Kconfig b/arch/sh/drivers/Kconfig
index 420c6b2..83c8794 100644
--- a/arch/sh/drivers/Kconfig
+++ b/arch/sh/drivers/Kconfig
@@ -16,4 +16,11 @@ config PUSH_SWITCH
          This enables support for the push switch framework, a simple
          framework that allows for sysfs driven switch status reporting.

+config SH_PWM_TPU
+       bool "PWM with Timer Pulse Unit (TPU) Driver"
+       depends on CPU_SUBTYPE_SH7723
+       select HAVE_PWM
+        help
+          Provides a PWM driver using the Timer Pulse Unit of the SH7723
+
 endmenu
diff --git a/arch/sh/drivers/Makefile b/arch/sh/drivers/Makefile
index e13f06b..12e9583 100644
--- a/arch/sh/drivers/Makefile
+++ b/arch/sh/drivers/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PCI)               += pci/
 obj-$(CONFIG_SUPERHYWAY)       += superhyway/
 obj-$(CONFIG_PUSH_SWITCH)      += push-switch.o
 obj-$(CONFIG_HEARTBEAT)                += heartbeat.o
+obj-$(CONFIG_SH_PWM_TPU)        += pwm_tpu.o
diff --git a/arch/sh/drivers/pwm_tpu.c b/arch/sh/drivers/pwm_tpu.c
new file mode 100644
index 0000000..d79733c
--- /dev/null
+++ b/arch/sh/drivers/pwm_tpu.c
@@ -0,0 +1,332 @@
+/*
+ * pwm_tpu.c
+ *
+ * Copyright (c) 2010 by emtrion GmbH
+ *
+ * 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.
+ *
+ * Author:     Markus Pietrek
+ * Description: Provides PWM functionality with the TPU.
+ *             TPU outputs 1 in disabled state and 0 in active state.
+ *
+ **/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#ifdef CONFIG_CPU_SUBTYPE_SH7723
+# include <cpu/sh7723.h>
+#endif
+
+/* TPU register definitions */
+#define TSTR           0x00
+
+/* TPU channel specific registers */
+#define TPUC_BASE      0x10
+#define TPUC_OFFSET    0x40
+
+#define TCR            0x00
+#define TMDR           0x04
+#define TIOR           0x08
+#define TIER           0x0C
+#define TSR            0x10
+#define TCNT           0x14
+#define TGRA           0x18
+#define TGRB           0x1C
+#define TGRC           0x20
+#define TGRD           0x24
+
+/* TPU register bits */
+#define TCR_CCLR_TGRB_CLEARS   0x40
+#define TCR_CKEG_RISING                0x00
+#define TCR_TPSC_64            0x03
+
+#define TIOR_OUTPUT_ALWAYS_0                   0x00
+#define TIOR_OUTPUT_ALWAYS_1                   0x04
+#define TIOR_INIT_0_OUTPUT_1_ON_MATCH_TGRA     0x02
+#define TIOR_INIT_1_OUTPUT_0_ON_MATCH_TGRA     0x05
+
+#define TMDR_PWM       0x02
+
+#define CHANNELS 4
+
+struct pwm_device {
+       const char *label;
+       int pwm_id;
+       int active;             /* for counting whether the clock on the TPU needs to be enabled */
+};
+
+static struct pwm_tpu_priv {
+       struct device *dev;
+       void __iomem *base;
+       struct clk *clk;
+       struct clk *bclk;
+       struct mutex mutex;
+
+       struct pwm_device devices[CHANNELS];
+       int clk_enabled;
+} *priv;
+
+static const int gpios[CHANNELS] = {
+#ifdef CONFIG_CPU_SUBTYPE_SH7723
+       GPIO_PTG0,
+       GPIO_PTG1,
+       GPIO_PTG2,
+       GPIO_PTG3,
+#else
+# error no GPIO configuration for cpu
+#endif
+};
+
+static inline void tpuc_write(const struct pwm_tpu_priv *priv, int ch, u16 val, int reg)
+{
+       iowrite16(val, priv->base + TPUC_BASE + (ch*TPUC_OFFSET) + reg);
+}
+
+static inline u16 tpuc_read(const struct pwm_tpu_priv *priv, int ch, int reg)
+{
+       return ioread16(priv->base + TPUC_BASE + (ch*TPUC_OFFSET) + reg);
+}
+
+/* exported pwm functions */
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+       struct pwm_device *pwm = NULL;
+       int error;
+
+       mutex_lock(&priv->mutex);
+
+       if (!label) {
+               dev_err(priv->dev, "need a name for pwm %i\n", pwm_id);
+               pwm = ERR_PTR(-EINVAL);
+               goto out;
+       }
+
+       if (pwm_id >= ARRAY_SIZE(priv->devices) || priv->devices[pwm_id].label) {
+               dev_err(priv->dev, "TPU %i already in use %s\n", pwm_id, priv->devices[pwm_id].label);
+               pwm = ERR_PTR(-EBUSY);
+               goto out;
+       }
+       pwm = &priv->devices[pwm_id];
+
+       error = gpio_request(gpios[pwm_id], label);
+       if (error) {
+               dev_err(priv->dev, "GPIO %i already reserved.\n", gpios[pwm_id] );
+               pwm = ERR_PTR(error);
+               goto out;
+       }
+       pwm->label  = label;
+       pwm->pwm_id = pwm_id;
+
+       dev_info(priv->dev, "TPU %i registered for %s\n", pwm_id, label);
+
+out:
+       mutex_unlock(&priv->mutex);
+       return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+       mutex_lock(&priv->mutex);
+       gpio_free(gpios[pwm->pwm_id]);
+       pwm->label = NULL;
+       mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL(pwm_free);
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+       int period_hz;
+       int tgra;
+       int tgrb;
+
+       BUG_ON((period_ns <= 0) || (duty_ns > period_ns));
+
+       mutex_lock(&priv->mutex);
+
+       /* enable clock for TPU when it was off before  */
+       if (!priv->clk_enabled) {
+               clk_enable(priv->clk);
+               priv->clk_enabled = 1;
+       }
+       pwm->active = 1;
+
+       /* calculate TGRA and TGRB settings based on the duty/period durations */
+       period_hz = 1000000000 / period_ns;
+       tgrb = ((clk_get_rate(priv->bclk)/64) / period_hz);
+
+       if (duty_ns >= period_ns)
+               tgra = 0;       /* 100% duty */
+       else if (duty_ns <= 0)
+               tgra = tgrb + 1; /* off */
+       else
+               tgra = tgrb - (tgrb * (duty_ns/1000))/(period_ns/1000);
+
+       /* configure TPU */
+       tpuc_write(priv, pwm->pwm_id,
+                  TCR_CCLR_TGRB_CLEARS |
+                  TCR_CKEG_RISING      |
+                  TCR_TPSC_64,
+                  TCR);
+       tpuc_write(priv, pwm->pwm_id,
+                  TIOR_INIT_1_OUTPUT_0_ON_MATCH_TGRA,
+                  TIOR);
+       tpuc_write(priv, pwm->pwm_id, tgra, TGRA);
+       tpuc_write(priv, pwm->pwm_id, tgrb, TGRB);
+       tpuc_write(priv, pwm->pwm_id, TMDR_PWM, TMDR);
+
+       mutex_unlock(&priv->mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+       /* pwm_config is called before enable, so we don't need to enable priv->clk here */
+       mutex_lock(&priv->mutex);
+       iowrite16(ioread16(priv->base+TSTR) | (1<<pwm->pwm_id), priv->base+TSTR);
+       mutex_unlock(&priv->mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+       int devices_active = 0;
+       int i;
+
+       mutex_lock(&priv->mutex);
+
+       /* set output to off state */
+       tpuc_write(priv, pwm->pwm_id,
+                  TIOR_OUTPUT_ALWAYS_1,
+                  TIOR);
+       iowrite16(ioread16(priv->base+TSTR) & ~(1<<pwm->pwm_id), priv->base+TSTR);
+
+       /* disable power when TPU is no longer in use */
+       pwm->active = 0;
+       for (i=0; i < ARRAY_SIZE(priv->devices); i++) {
+               if (priv->devices[i].active) {
+                       devices_active = 1;
+                       break;
+               }
+       }
+
+       if (!devices_active) {
+               clk_disable(priv->clk);
+               priv->clk_enabled = 0;
+       }
+
+       mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL(pwm_disable);
+
+/* driver registering functions */
+
+static int __devinit pwm_tpu_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int error;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(&pdev->dev, "cannot get memory\n");
+               error = -ENOMEM;
+               goto error;
+       }
+       priv->dev = &pdev->dev;
+       platform_set_drvdata(pdev, priv);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "cannot get platform resources\n");
+               error = -EINVAL;
+               goto error_map;
+       }
+
+       priv->base = ioremap(res->start, (res->end-res->start)+1);
+       if (!priv->base) {
+               dev_err(&pdev->dev, "cannot get io memory\n");
+               error = -ENOMEM;
+               goto error_map;
+       }
+
+       priv->clk = clk_get(&pdev->dev, "tpu0");
+       if (IS_ERR(priv->clk)) {
+               dev_err(&pdev->dev, "cannot get clock");
+               goto error_clock;
+       }
+
+       priv->bclk = clk_get(&pdev->dev, "bus_clk");
+       if (IS_ERR(priv->bclk)) {
+               dev_err(&pdev->dev, "cannot get bus clock");
+               goto error_bclock;
+       }
+
+       mutex_init(&priv->mutex);
+
+       dev_info(&pdev->dev, "initialized\n");
+       return 0;
+
+error_bclock:
+       clk_put(priv->bclk);
+
+error_clock:
+       iounmap(priv->base);
+
+error_map:
+       kfree(priv);
+
+error:
+       return error;
+}
+
+static int __devexit pwm_tpu_remove(struct platform_device *pdev)
+{
+       clk_put(priv->bclk);
+       clk_put(priv->clk);
+       iounmap(priv->base);
+       kfree(priv);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver pwm_tpu_driver = {
+       .probe          = pwm_tpu_probe,
+       .remove         = pwm_tpu_remove,
+       .driver         = {
+               .name   = "pwm_tpu",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init pwm_tpu_init(void)
+{
+       return platform_driver_register(&pwm_tpu_driver);
+}
+
+static void __exit pwm_tpu_exit(void)
+{
+       platform_driver_unregister(&pwm_tpu_driver);
+}
+
+MODULE_AUTHOR("Markus Pietrek");
+MODULE_DESCRIPTION("PWM/Timer Pulse Unit TPU control");
+MODULE_LICENSE("GPL");
+
+arch_initcall(pwm_tpu_init);
+module_exit(pwm_tpu_exit);
--
1.5.4.3


_____________________________________

Amtsgericht Mannheim
HRB 110 300
Gesch?ftsf?hrer: Dieter Baur, Ramona Maurer
_____________________________________

Important Note:
- This e-mail may contain trade secrets or privileged, undisclosed or otherwise confidential information.
- If you have received this e-mail in error, you are hereby notified that any review, copying or distribution of it is strictly prohibited.
- Please inform us immediately and destroy the original transmittal.

Thank you for your cooperation.

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] sh: added PWM driver for SH7723 using the TPU
  2010-02-03 13:05 [PATCH] sh: added PWM driver for SH7723 using the TPU Pietrek, Markus
  2010-02-03 14:35 ` AW: " Pietrek, Markus
@ 2010-02-03 23:39 ` Paul Mundt
  2010-02-04  8:11 ` AW: " Pietrek, Markus
  2 siblings, 0 replies; 4+ messages in thread
From: Paul Mundt @ 2010-02-03 23:39 UTC (permalink / raw)
  To: linux-sh

On Wed, Feb 03, 2010 at 03:35:45PM +0100, Pietrek, Markus wrote:
> diff --git a/arch/sh/drivers/Kconfig b/arch/sh/drivers/Kconfig
> index 420c6b2..83c8794 100644
> --- a/arch/sh/drivers/Kconfig
> +++ b/arch/sh/drivers/Kconfig
> @@ -16,4 +16,11 @@ config PUSH_SWITCH
>           This enables support for the push switch framework, a simple
>           framework that allows for sysfs driven switch status reporting.
> 
> +config SH_PWM_TPU
> +       bool "PWM with Timer Pulse Unit (TPU) Driver"
> +       depends on CPU_SUBTYPE_SH7723
> +       select HAVE_PWM
> +        help
> +          Provides a PWM driver using the Timer Pulse Unit of the SH7723
> +
The cleaner way to do this would be to have something like:

config SYS_SUPPORTS_TPU
	bool

in arch/sh/Kconfig, followed by having SH7723 select that. Then your
SH_PMW_TPU config option can just depend on SYS_SUPPORTS_TPU, and it will
be automatically visible for any future CPU that indicates block support.

> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/gpio.h>
> +#include <linux/io.h>
> +#include <linux/pwm.h>
> +
> +#ifdef CONFIG_CPU_SUBTYPE_SH7723
> +# include <cpu/sh7723.h>
> +#endif
> +
Presumably you wanted this header for ..

> +static const int gpios[CHANNELS] = {
> +#ifdef CONFIG_CPU_SUBTYPE_SH7723
> +       GPIO_PTG0,
> +       GPIO_PTG1,
> +       GPIO_PTG2,
> +       GPIO_PTG3,
> +#else
> +# error no GPIO configuration for cpu
> +#endif
> +};
> +
this. However, you should simply construct the GPIO map through platform
data and pass it along when registering the device from the CPU code.

> +EXPORT_SYMBOL(pwm_disable);
> +
Is EXPORT_SYMBOL() intentional? By convention we primarily use
EXPORT_SYMBOL_GPL() for new exports.

> +/* driver registering functions */
> +
> +static int __devinit pwm_tpu_probe(struct platform_device *pdev)
> +{
[snip]

> +       priv->base = ioremap(res->start, (res->end-res->start)+1);

Although you didn't succumb to the off-by-1 bug that most people do, you
should still use resource_size() here to avoid ambiguity. :-)

> +MODULE_AUTHOR("Markus Pietrek");
> +MODULE_DESCRIPTION("PWM/Timer Pulse Unit TPU control");
> +MODULE_LICENSE("GPL");
> +
> +arch_initcall(pwm_tpu_init);
> +module_exit(pwm_tpu_exit);

arch_initcall() is a bit early, presumably you have some users of this
code that actually needs to have this initialized that early on?

Looks good otherwise.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* AW: [PATCH] sh: added PWM driver for SH7723 using the TPU
  2010-02-03 13:05 [PATCH] sh: added PWM driver for SH7723 using the TPU Pietrek, Markus
  2010-02-03 14:35 ` AW: " Pietrek, Markus
  2010-02-03 23:39 ` Paul Mundt
@ 2010-02-04  8:11 ` Pietrek, Markus
  2 siblings, 0 replies; 4+ messages in thread
From: Pietrek, Markus @ 2010-02-04  8:11 UTC (permalink / raw)
  To: linux-sh

Hi Paul,

Thank you for your comments. Yet the 2 issues listed below are inherited from the only other pwm implementations in arch/arm/plat-{pxa,s3c,mxc}.

> > +EXPORT_SYMBOL(pwm_disable);
> > +
> Is EXPORT_SYMBOL() intentional? By convention we primarily use
> EXPORT_SYMBOL_GPL() for new exports.

And

> arch_initcall() is a bit early, presumably you have some users of this
> code that actually needs to have this initialized that early on?

Peronally I would have used subsys_initcall if it was not for the other implementations.

Best regards,
Markus

_____________________________________

Amtsgericht Mannheim
HRB 110 300
Gesch?ftsf?hrer: Dieter Baur, Ramona Maurer
_____________________________________

Important Note:
- This e-mail may contain trade secrets or privileged, undisclosed or otherwise confidential information.
- If you have received this e-mail in error, you are hereby notified that any review, copying or distribution of it is strictly prohibited.
- Please inform us immediately and destroy the original transmittal.

Thank you for your cooperation.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2010-02-04  8:11 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-02-03 13:05 [PATCH] sh: added PWM driver for SH7723 using the TPU Pietrek, Markus
2010-02-03 14:35 ` AW: " Pietrek, Markus
2010-02-03 23:39 ` Paul Mundt
2010-02-04  8:11 ` AW: " Pietrek, Markus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).