summaryrefslogtreecommitdiffstats
path: root/drivers/pwm
diff options
context:
space:
mode:
authorRobert Jarzmik <robert.jarzmik@free.fr>2012-02-16 19:23:47 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2012-02-17 10:18:30 +0100
commitbf841a598e0ce6550c477875fe8567bc507d8d23 (patch)
tree641cd4d6a9f58af1cc2d6723f979ddb0a67731f3 /drivers/pwm
parent6fa3e3119ff2864dac131933a1abfe201a264a0a (diff)
downloadbarebox-bf841a598e0ce6550c477875fe8567bc507d8d23.tar.gz
barebox-bf841a598e0ce6550c477875fe8567bc507d8d23.tar.xz
drivers/pwm: add PXA pulse width modulator controller
Add PXA embedded pulse width modulator support. The PWM can generate signals from 49.6kHz to 1.625MHz. The driver is for pxa2xx family. The pxa3xx was not handled yet. Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/pwm')
-rw-r--r--drivers/pwm/Kconfig6
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pxa_pwm.c157
3 files changed, 164 insertions, 0 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 93c1052291..50c956a67d 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -8,5 +8,11 @@ menuconfig PWM
If unsure, say N.
if PWM
+config PWM_PXA
+ bool "PXA PWM Support"
+ default y if ARCH_PXA2XX
+ help
+ This enables PWM support for Intel/Marvell PXA chips, such
+ as the PXA25x, PXA27x.
endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 3469c3d28b..c886bd55bf 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_PWM) += core.o
+obj-$(CONFIG_PWM_PXA) += pxa_pwm.o \ No newline at end of file
diff --git a/drivers/pwm/pxa_pwm.c b/drivers/pwm/pxa_pwm.c
new file mode 100644
index 0000000000..cf1f96a10a
--- /dev/null
+++ b/drivers/pwm/pxa_pwm.c
@@ -0,0 +1,157 @@
+/*
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 2008-02-13 initial version eric miao <eric.miao@marvell.com>
+ * 2012 Robert Jarzmik <robert.jarzmik@free.fr>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <errno.h>
+#include <io.h>
+#include <pwm.h>
+
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/pxa-regs.h>
+#include <mach/regs-pwm.h>
+#include <asm-generic/div64.h>
+#include <linux/compiler.h>
+
+/* PWM registers and bits definitions */
+#define PWMCR (0x00)
+#define PWMDCR (0x04)
+#define PWMPCR (0x08)
+
+#define PWMCR_SD (1 << 6)
+#define PWMDCR_FD (1 << 10)
+
+struct pxa_pwm_chip {
+ struct pwm_chip chip;
+ void __iomem *iobase;
+ int id;
+ int duty_ns;
+ int period_ns;
+};
+
+static struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct pxa_pwm_chip, chip);
+}
+
+/*
+ * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
+ * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ * PWM_CLK_RATE = 13 MHz
+ */
+static int pxa_pwm_config(struct pwm_chip *chip, int duty_ns, int period_ns)
+{
+ unsigned long long c;
+ unsigned long period_cycles, prescale, pv, dc;
+ struct pxa_pwm_chip *pxa_pwm = to_pxa_pwm_chip(chip);
+
+ c = pxa_get_pwmclk();
+ c = c * period_ns;
+ do_div(c, 1000000000);
+ period_cycles = c;
+
+ if (period_cycles < 1)
+ period_cycles = 1;
+ prescale = (period_cycles - 1) / 1024;
+ pv = period_cycles / (prescale + 1) - 1;
+
+ if (prescale > 63)
+ return -EINVAL;
+
+ if (duty_ns == period_ns)
+ dc = PWMDCR_FD;
+ else
+ dc = (pv + 1) * duty_ns / period_ns;
+
+ pxa_pwm->duty_ns = duty_ns;
+ pxa_pwm->period_ns = period_ns;
+
+ /* NOTE: the clock to PWM has to be enabled first
+ * before writing to the registers
+ */
+ __raw_writel(prescale, pxa_pwm->iobase + PWMCR);
+ __raw_writel(dc, pxa_pwm->iobase + PWMDCR);
+ __raw_writel(pv, pxa_pwm->iobase + PWMPCR);
+
+ return 0;
+}
+
+static int pxa_pwm_enable(struct pwm_chip *chip)
+{
+ struct pxa_pwm_chip *pxa_pwm = to_pxa_pwm_chip(chip);
+
+ switch (pxa_pwm->id) {
+ case 0:
+ case 2:
+ CKEN |= CKEN_PWM0;
+ break;
+ case 1:
+ case 3:
+ CKEN |= CKEN_PWM1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void pxa_pwm_disable(struct pwm_chip *chip)
+{
+ struct pxa_pwm_chip *pxa_pwm = to_pxa_pwm_chip(chip);
+
+ switch (pxa_pwm->id) {
+ case 0:
+ case 2:
+ CKEN &= ~CKEN_PWM0;
+ break;
+ case 1:
+ case 3:
+ CKEN &= ~CKEN_PWM1;
+ break;
+ default:
+ break;
+ }
+}
+
+static struct pwm_ops pxa_pwm_ops = {
+ .config = pxa_pwm_config,
+ .enable = pxa_pwm_enable,
+ .disable = pxa_pwm_disable,
+};
+
+static int pxa_pwm_probe(struct device_d *dev)
+{
+ struct pxa_pwm_chip *chip;
+
+ chip = xzalloc(sizeof(*chip));
+ chip->chip.devname = asprintf("%s", dev_name(dev));
+ chip->chip.ops = &pxa_pwm_ops;
+ chip->iobase = dev_request_mem_region(dev, 0);
+ chip->id = dev->id;
+ dev->priv = chip;
+
+ return pwmchip_add(&chip->chip, dev);
+}
+
+static struct driver_d pxa_pwm_driver = {
+ .name = "pxa_pwm",
+ .probe = pxa_pwm_probe,
+};
+
+static int __init pxa_pwm_init_driver(void)
+{
+ CKEN &= ~CKEN_PWM0 & ~CKEN_PWM1;
+ register_driver(&pxa_pwm_driver);
+ return 0;
+}
+
+device_initcall(pxa_pwm_init_driver);