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>] [day] [month] [year] [list]
Message-ID: <8447d6730901080207i5dffd0c7t6c0d1e1f8aee4c9d@mail.gmail.com>
Date:	Thu, 8 Jan 2009 11:07:11 +0100
From:	"Davide Rizzo" <elpa.rizzo@...il.com>
To:	linux-kernel@...r.kernel.org, hjk@...utronix.de, gregkh@...e.de,
	ben-linux@...ff.org
Subject: [PATCH 2/2] Driver for user access to internal timer

Adds the function pwm_one_shot to the pwm interface, to use a timer to wait for
 a short period and call a callback at the end. This is for ms range delays.
There is also the implementation for the Samsung S3C24xx platform.

Signed-off-by: Davide Rizzo <elpa-rizzo@...il.com>
---
diff -urNp linux-2.6.28/arch/arm/plat-s3c24xx/pwm.c
linux-2.6.28.elpa/arch/arm/plat-s3c24xx/pwm.c
--- linux-2.6.28/arch/arm/plat-s3c24xx/pwm.c	2008-12-25 00:26:37.000000000 +0100
+++ linux-2.6.28.elpa/arch/arm/plat-s3c24xx/pwm.c	2009-01-06
19:04:44.000000000 +0100
@@ -15,6 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
+#include <linux/interrupt.h>
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/pwm.h>
@@ -22,6 +23,8 @@
 #include <plat/devs.h>
 #include <plat/regs-timer.h>

+#define DEV_NAME "s3c24xx-pwm"
+
 struct pwm_device {
 	struct list_head	 list;
 	struct platform_device	*pdev;
@@ -37,6 +40,11 @@ struct pwm_device {
 	unsigned char		 running;
 	unsigned char		 use_count;
 	unsigned char		 pwm_id;
+
+	/* For one-shot mode callback*/
+	int irq;
+	void (*expired)(struct pwm_device *, void *arg);
+	void *arg;
 };

 #define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg)
@@ -57,7 +65,7 @@ static struct clk *clk_scaler[2];
 	}

 #define DEFINE_S3C_TIMER(_tmr_no, _irq)			\
-	.name		= "s3c24xx-pwm",		\
+	.name		= DEV_NAME,		\
 	.id		= _tmr_no,			\
 	.num_resources	= TIMER_RESOURCE_SIZE,		\
 	.resource	= TIMER_RESOURCE(_tmr_no, _irq),	\
@@ -272,6 +280,88 @@ int pwm_config(struct pwm_device *pwm, i

 EXPORT_SYMBOL(pwm_config);

+static irqreturn_t timer_isr(int irq, void *arg)
+{
+	struct pwm_device *pwm = arg;
+
+	if (pwm->expired) {
+		disable_irq(irq);
+		pwm_disable(pwm);
+		pwm->expired(pwm, pwm->arg);
+		pwm->expired = NULL;
+	}
+	return IRQ_HANDLED;
+}
+
+int pwm_one_shot(struct pwm_device *pwm, int timeout_ns,
+	void(*expired)(struct pwm_device *, void *arg), void *arg)
+{
+	unsigned long tin_rate;
+	unsigned long tin_ns;
+	unsigned long period;
+	unsigned long flags;
+	unsigned long tcon;
+	unsigned long tcnt;
+
+	/* We currently avoid using 64bit arithmetic by using the
+	 * fact that anything faster than 1Hz is easily representable
+	 * by 32bits. */
+
+	if (timeout_ns > NS_IN_HZ)
+		return -ERANGE;
+
+	/* The TCMP and TCNT can be read without a lock, they're not
+	 * shared between the timers. */
+
+	tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id));
+
+	period = NS_IN_HZ / timeout_ns;
+
+	pwm_dbg(pwm, "one shot timeout_ns=%d\n", timeout_ns);
+
+	if (pwm_is_tdiv(pwm)) {
+		tin_rate = pwm_calc_tin(pwm, period);
+		clk_set_rate(pwm->clk_div, tin_rate);
+	} else
+		tin_rate = clk_get_rate(pwm->clk);
+
+	pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate);
+
+	tin_ns = NS_IN_HZ / tin_rate;
+	tcnt = timeout_ns / tin_ns;
+	/* Note, counters count down */
+
+	pwm_dbg(pwm, "tin_ns=%lu\n", tin_ns);
+
+	/* Update the PWM register block. */
+
+	local_irq_save(flags);
+
+	if (!pwm->expired)
+		enable_irq(pwm->irq);
+
+	__raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id));
+
+	tcon = __raw_readl(S3C2410_TCON);
+	tcon |= pwm_tcon_manulupdate(pwm);
+	tcon &= ~pwm_tcon_autoreload(pwm);
+	tcon &= ~pwm_tcon_start(pwm);
+	__raw_writel(tcon, S3C2410_TCON);
+
+
+	pwm->expired = expired;
+	pwm->arg = arg;
+
+	tcon &= ~pwm_tcon_manulupdate(pwm);
+	tcon |= pwm_tcon_start(pwm);
+	__raw_writel(tcon, S3C2410_TCON);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_one_shot);
+
 static int pwm_register(struct pwm_device *pwm)
 {
 	pwm->duty_ns = -1;
@@ -288,6 +378,7 @@ static int s3c_pwm_probe(struct platform
 {
 	struct device *dev = &pdev->dev;
 	struct pwm_device *pwm;
+	struct resource *res;
 	unsigned long flags;
 	unsigned long tcon;
 	unsigned int id = pdev->id;
@@ -306,6 +397,22 @@ static int s3c_pwm_probe(struct platform

 	pwm->pdev = pdev;
 	pwm->pwm_id = id;
+	pwm->expired = NULL;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(dev, "cannot get platform interrupt resource\n");
+		ret = -EBUSY;
+		goto err_getresources;
+	}
+	pwm->irq = res->start;
+	ret = request_irq(pwm->irq, timer_isr, IRQF_DISABLED,
+		DEV_NAME, pwm);
+	if (ret) {
+		dev_err(dev, "request_irq failed\n");
+		goto err_getirq;
+	}
+	disable_irq(pwm->irq);

 	/* calculate base of control bits in TCON */
 	pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;
@@ -354,6 +461,10 @@ static int s3c_pwm_probe(struct platform
 	clk_put(pwm->clk_div);

  err_clk_tin:
+ err_getresources:
+	free_irq(pwm->irq, DEV_NAME);
+
+ err_getirq:
 	clk_put(pwm->clk);

  err_alloc:
@@ -365,6 +476,7 @@ static int s3c_pwm_remove(struct platfor
 {
 	struct pwm_device *pwm = platform_get_drvdata(pdev);

+	free_irq(pwm->irq, DEV_NAME);
 	clk_put(pwm->clk_div);
 	clk_put(pwm->clk);
 	kfree(pwm);
@@ -374,7 +486,7 @@ static int s3c_pwm_remove(struct platfor

 static struct platform_driver s3c_pwm_driver = {
 	.driver		= {
-		.name	= "s3c24xx-pwm",
+		.name	= DEV_NAME,
 		.owner	= THIS_MODULE,
 	},
 	.probe		= s3c_pwm_probe,
diff -urNp linux-2.6.28/include/linux/pwm.h
linux-2.6.28.elpa/include/linux/pwm.h
--- linux-2.6.28/include/linux/pwm.h	2008-12-25 00:26:37.000000000 +0100
+++ linux-2.6.28.elpa/include/linux/pwm.h	2009-01-06 19:35:19.000000000 +0100
@@ -19,6 +19,13 @@ void pwm_free(struct pwm_device *pwm);
 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);

 /*
+ * pwm_one_shot - programs and starts a timer for one-shot operation
+ *  when expired calls callback
+ */
+int pwm_one_shot(struct pwm_device *pwm, int timeout_ns,
+	void(*expired)(struct pwm_device *, void *arg), void *arg);
+
+/*
  * pwm_enable - start a PWM output toggling
  */
 int pwm_enable(struct pwm_device *pwm);
--
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