lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <c6c7ccbedbade4c8e4695f914d065d40b646587e.1265748264.git.bgat@billgatliff.com>
Date:	Tue,  9 Feb 2010 14:46:34 -0600
From:	Bill Gatliff <bgat@...lgatliff.com>
To:	linux-embedded@...r.kernel.org
Cc:	linux-kernel@...r.kernel.org, Bill Gatliff <bgat@...lgatliff.com>
Subject: [PWM PATCH 7/7] PWM API driver for MPC52xx GPT peripheral


Signed-off-by: Bill Gatliff <bgat@...lgatliff.com>
---
 arch/powerpc/platforms/52xx/mpc52xx_gpt.c |  195 ++++++++++++++++++++++++++++-
 1 files changed, 193 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
index 6f8ebe1..b9aa4a5 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
@@ -1,6 +1,7 @@
 /*
  * MPC5200 General Purpose Timer device driver
  *
+ * Copyright (c) 2010 Bill Gatliff <bgat@...lgatliff.com>
  * Copyright (c) 2009 Secret Lab Technologies Ltd.
  * Copyright (c) 2008 Sascha Hauer <s.hauer@...gutronix.de>, Pengutronix
  *
@@ -50,6 +51,12 @@
  * IO, or it can be an Open Collector (OC) output.  At the moment it is the
  * responsibility of either the bootloader or the platform setup code to set
  * the output mode.  This driver does not change the output mode setting.
+ *
+ * To use the PWM function, the following property must be added to
+ * the device tree node for the gpt device:
+ *     pwm-controller;
+ * TODO: we could set polarity, initial period and duty cycle, on/off,
+ * and whatnot inside the dts file...
  */
 
 #include <linux/device.h>
@@ -65,11 +72,12 @@
 #include <linux/watchdog.h>
 #include <linux/miscdevice.h>
 #include <linux/uaccess.h>
+#include <linux/pwm/pwm.h>
 #include <asm/div64.h>
 #include <asm/mpc52xx.h>
 
 MODULE_DESCRIPTION("Freescale MPC52xx gpt driver");
-MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dreß");
+MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dreß, Bill Gatliff");
 MODULE_LICENSE("GPL");
 
 /**
@@ -95,6 +103,9 @@ struct mpc52xx_gpt_priv {
 #if defined(CONFIG_GPIOLIB)
 	struct of_gpio_chip of_gc;
 #endif
+#if defined(CONFIG_GENERIC_PWM)
+	struct pwm_device pwm;
+#endif
 };
 
 LIST_HEAD(mpc52xx_gpt_list);
@@ -125,6 +136,10 @@ DEFINE_MUTEX(mpc52xx_gpt_list_mutex);
 
 #define MPC52xx_GPT_STATUS_IRQMASK	(0x000f)
 
+#define MPC52xx_GPT_PWM_WIDTH_MASK	(0xffff0000)
+#define MPC52xx_GPT_PWM_PWMOP		(0x100)
+#define MPC52xx_GPT_PWM_LOAD		(0x1)
+
 #define MPC52xx_GPT_CAN_WDT		(1 << 0)
 #define MPC52xx_GPT_IS_WDT		(1 << 1)
 
@@ -274,6 +289,182 @@ mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node)
 
 
 /* ---------------------------------------------------------------------
+ * PWM API hooks
+ */
+#if defined(CONFIG_GENERIC_PWM)
+static inline struct mpc52xx_gpt_priv *pwm_to_mpc52xx_gpt(const struct pwm_channel *p)
+{
+	return container_of(p->pwm, struct mpc52xx_gpt_priv, pwm);
+}
+
+static int mpc52xx_gpt_pwm_request(struct pwm_channel *p)
+{
+	struct mpc52xx_gpt_priv *gpt = pwm_to_mpc52xx_gpt(p);
+
+	/* TODO: add hooks to prevent conflicts in use */
+	p->tick_hz = gpt->ipb_freq;
+	return 0;
+}
+
+static void mpc52xx_gpt_pwm_free(struct pwm_channel *p)
+{
+	/* TODO: add hooks to prevent conflicts in use */
+}
+
+static int __mpc52xx_gpt_pwm_config_period_ticks(struct pwm_channel *p,
+					       struct pwm_channel_config *c)
+{
+	struct mpc52xx_gpt_priv *gpt = pwm_to_mpc52xx_gpt(p);
+	u64 prescale, count;
+
+	prescale = (c->period_ticks >> 16) + 1;
+	count = c->period_ticks;
+	do_div(count, prescale);
+	out_be32(&gpt->regs->count, prescale << 16 | count);
+
+	p->period_ticks = count * prescale;
+	dev_dbg(p->pwm->dev, "prescale %4x count %4x period_ticks %8x\n",
+		(unsigned int)prescale, (unsigned int)count,
+		(unsigned int)p->period_ticks);
+
+	return 0;
+}
+
+static int __mpc52xx_gpt_pwm_config_duty_ticks(struct pwm_channel *p,
+					       struct pwm_channel_config *c)
+{
+	struct mpc52xx_gpt_priv *gpt = pwm_to_mpc52xx_gpt(p);
+	unsigned long flags;
+	u64 width = c->duty_ticks;
+	u32 prescale, count;
+
+	spin_lock_irqsave(&gpt->lock, flags);
+
+	count = in_be32(&gpt->regs->count);
+	prescale = count >> 16;
+	count &= 0xffffUL;
+
+	/* TODO: this probably isn't the best place to do a divide... */
+	do_div(width, prescale);
+
+	clrsetbits_be32(&gpt->regs->pwm, MPC52xx_GPT_PWM_WIDTH_MASK,
+			MPC52xx_GPT_PWM_WIDTH_MASK & (width << 16));
+	spin_unlock_irqrestore(&gpt->lock, flags);
+
+	p->duty_ticks = width * prescale;
+	dev_dbg(p->pwm->dev, "prescale %4x count %4x width %4x duty_ticks %8x\n",
+		(unsigned int)prescale, (unsigned int)count,
+		(unsigned int)width, (unsigned int)p->duty_ticks);
+
+	return 0;
+}
+
+static int __mpc52xx_gpt_pwm_config_polarity(struct pwm_channel *p,
+					     struct pwm_channel_config *c)
+{
+	struct mpc52xx_gpt_priv *gpt = pwm_to_mpc52xx_gpt(p);
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpt->lock, flags);
+	if (c->polarity)
+		setbits32(&gpt->regs->pwm, MPC52xx_GPT_PWM_PWMOP);
+	else
+		clrbits32(&gpt->regs->pwm, MPC52xx_GPT_PWM_PWMOP);
+	spin_unlock_irqrestore(&gpt->lock, flags);
+	return 0;
+}
+
+static int __mpc52xx_gpt_pwm_start(struct pwm_channel *p)
+{
+	struct mpc52xx_gpt_priv *gpt = pwm_to_mpc52xx_gpt(p);
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpt->lock, flags);
+	clrsetbits_be32(&gpt->regs->mode, MPC52xx_GPT_MODE_MS_MASK,
+		     MPC52xx_GPT_MODE_MS_PWM);
+	spin_unlock_irqrestore(&gpt->lock, flags);
+	return 0;
+}
+
+static int __mpc52xx_gpt_pwm_stop(struct pwm_channel *p)
+{
+	struct mpc52xx_gpt_priv *gpt = pwm_to_mpc52xx_gpt(p);
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpt->lock, flags);
+	clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_MS_MASK);
+	spin_unlock_irqrestore(&gpt->lock, flags);
+	return 0;
+}
+
+static int mpc52xx_gpt_pwm_config_nosleep(struct pwm_channel *p,
+					  struct pwm_channel_config *c)
+{
+	int ret = -EINVAL;
+
+	if (c->config_mask & PWM_CONFIG_PERIOD_TICKS)
+		if ((ret = __mpc52xx_gpt_pwm_config_period_ticks(p, c)))
+			goto done;
+
+	if (c->config_mask & PWM_CONFIG_DUTY_TICKS)
+		if ((ret = __mpc52xx_gpt_pwm_config_duty_ticks(p, c)))
+			goto done;
+
+	if (c->config_mask & PWM_CONFIG_POLARITY)
+		if ((ret = __mpc52xx_gpt_pwm_config_polarity(p, c)))
+			goto done;
+
+	if (c->config_mask & PWM_CONFIG_START)
+		if ((ret = __mpc52xx_gpt_pwm_start(p)))
+			goto done;
+
+	if (c->config_mask & PWM_CONFIG_STOP)
+		if ((ret = __mpc52xx_gpt_pwm_stop(p)))
+			goto done;
+
+done:
+	return ret;
+}
+
+static int mpc52xx_gpt_pwm_config(struct pwm_channel *p,
+				  struct pwm_channel_config *c)
+{
+	dev_dbg(p->pwm->dev, "config_mask %x\n", c->config_mask);
+
+	if (!mpc52xx_gpt_pwm_config_nosleep(p, c))
+		return 0;
+
+	might_sleep();
+
+	/* TODO: add other API entry points */
+
+	return -EINVAL;
+}
+
+static void
+mpc52xx_gpt_pwm_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node)
+{
+	if (!of_find_property(node, "pwm-controller", NULL))
+		return;
+
+	gpt->pwm.dev = gpt->dev;
+	gpt->pwm.bus_id = dev_name(gpt->dev);
+	gpt->pwm.nchan = 1;
+	gpt->pwm.owner = THIS_MODULE;
+
+	gpt->pwm.request = mpc52xx_gpt_pwm_request;
+	gpt->pwm.free = mpc52xx_gpt_pwm_free;
+	gpt->pwm.config_nosleep = mpc52xx_gpt_pwm_config_nosleep;
+	gpt->pwm.config = mpc52xx_gpt_pwm_config;
+
+	pwm_register(&gpt->pwm);
+}
+#else
+static void
+mpc52xx_gpt_pwm_setup(struct mpc52xx_gpt_priv *p, struct device_node *np) {}
+#endif
+
+/* ---------------------------------------------------------------------
  * GPIOLIB hooks
  */
 #if defined(CONFIG_GPIOLIB)
@@ -737,9 +928,9 @@ static int __devinit mpc52xx_gpt_probe(struct of_device *ofdev,
 	}
 
 	dev_set_drvdata(&ofdev->dev, gpt);
-
 	mpc52xx_gpt_gpio_setup(gpt, ofdev->node);
 	mpc52xx_gpt_irq_setup(gpt, ofdev->node);
+	mpc52xx_gpt_pwm_setup(gpt, ofdev->node);
 
 	mutex_lock(&mpc52xx_gpt_list_mutex);
 	list_add(&gpt->list, &mpc52xx_gpt_list);
-- 
1.6.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ