From patchwork Tue Aug 28 06:39:19 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Sascha Hauer X-Patchwork-Id: 1379221 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 049493FCAE for ; Tue, 28 Aug 2012 06:42:27 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1T6FSU-0006yD-GI; Tue, 28 Aug 2012 06:39:30 +0000 Received: from metis.ext.pengutronix.de ([2001:6f8:1178:4:290:27ff:fe1d:cc33]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1T6FSP-0006xz-FQ for linux-arm-kernel@lists.infradead.org; Tue, 28 Aug 2012 06:39:27 +0000 Received: from dude.hi.pengutronix.de ([2001:6f8:1178:2:21e:67ff:fe11:9c5c]) by metis.ext.pengutronix.de with esmtp (Exim 4.72) (envelope-from ) id 1T6FSK-0007nc-IO; Tue, 28 Aug 2012 08:39:20 +0200 Received: from sha by dude.hi.pengutronix.de with local (Exim 4.80) (envelope-from ) id 1T6FSJ-0004Zl-NC; Tue, 28 Aug 2012 08:39:19 +0200 Date: Tue, 28 Aug 2012 08:39:19 +0200 From: Sascha Hauer To: HACHIMI Samir Subject: Re: [PATCH v2 0/2] imx6q: pwm: Activate stop_mode and configure pinmux Message-ID: <20120828063919.GN26594@pengutronix.de> References: <3465D313FDFB824F9A9C8CD24FA4F6BCB4ACFC@frontmail.adetel.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <3465D313FDFB824F9A9C8CD24FA4F6BCB4ACFC@frontmail.adetel.com> X-Sent-From: Pengutronix Hildesheim X-URL: http://www.pengutronix.de/ X-IRC: #ptxdist @freenode X-Accept-Language: de,en X-Accept-Content-Type: text/plain X-Uptime: 08:32:38 up 65 days, 21:44, 28 users, load average: 0.04, 0.10, 0.21 User-Agent: Mutt/1.5.21 (2010-09-15) X-SA-Exim-Connect-IP: 2001:6f8:1178:2:21e:67ff:fe11:9c5c X-SA-Exim-Mail-From: sha@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-arm-kernel@lists.infradead.org X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.1 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.1 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.2 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: shawn.guo@linaro.org, thierry.reding@avionic-design.de, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Hi, On Mon, Aug 27, 2012 at 05:34:30PM +0200, HACHIMI Samir wrote: > From: Samir Hachimi > > This patch series enables support for pwm driver on imx6q SoC. The first > patch of the series configure the pinctrl for pwm in device-tree. Actually > they are several pin who can be set to pwmO for each pwm, so I assume that > all of them must be enabled when configuring pin. > The second patch activate stop_enable mode and unset the enable bit during > configuration of pwm, and set the enable bit in pwm CR register during enable. > > Changes since v1: > Remove default pwm pinmux configuration in DT as Shawn and Matt suggested. > Now, pwm used pwm's clocks properties to configure pwm's clock. > Need Shawn's patch '[PATCH v2 0/4] Move imx6q/28/23 clock lookup over to device tree' to work. > Use Benoît's '[PATCH] pwm-imx: Fix config / enable / disable' (un)setting the cr enable bit to enable/disable pwm. > The second patch used driver_data instead of cpu_is_xxx macros, to check used architecture and enabled stop_enable mode. > Before doing anything else to this driver I suggest the following patch. It makes further changes and cleanups easier. Also, I have a second patch here which consistently switches the pwm to use the ipg clock for its output instead of the per clock. This eliminates the use of another cpu_is_*. The patch is not ready though because I read some thread about the clock being disabled during pwm_config. Sascha 8<------------------------------------------------------------------- pwm i.MX: factor out cpu specific functions To make the code more flexible. Signed-off-by: Sascha Hauer --- drivers/pwm/pwm-imx.c | 145 ++++++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 63 deletions(-) diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 2a0b353..38270f4 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -46,81 +46,95 @@ struct imx_chip { void __iomem *mmio_base; struct pwm_chip chip; + + int (*config)(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns); }; #define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) -static int imx_pwm_config(struct pwm_chip *chip, +static int imx_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct imx_chip *imx = to_imx_chip(chip); - if (!(cpu_is_mx1() || cpu_is_mx21())) { - unsigned long long c; - unsigned long period_cycles, duty_cycles, prescale; - u32 cr; - - c = clk_get_rate(imx->clk); - c = c * period_ns; - do_div(c, 1000000000); - period_cycles = c; - - prescale = period_cycles / 0x10000 + 1; - - period_cycles /= prescale; - c = (unsigned long long)period_cycles * duty_ns; - do_div(c, period_ns); - duty_cycles = c; - - /* - * according to imx pwm RM, the real period value should be - * PERIOD value in PWMPR plus 2. - */ - if (period_cycles > 2) - period_cycles -= 2; - else - period_cycles = 0; - - writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); - writel(period_cycles, imx->mmio_base + MX3_PWMPR); - - cr = MX3_PWMCR_PRESCALER(prescale) | - MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | - MX3_PWMCR_DBGEN | MX3_PWMCR_EN; - - if (cpu_is_mx25()) - cr |= MX3_PWMCR_CLKSRC_IPG; - else - cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; - - writel(cr, imx->mmio_base + MX3_PWMCR); - } else if (cpu_is_mx1() || cpu_is_mx21()) { - /* The PWM subsystem allows for exact frequencies. However, - * I cannot connect a scope on my device to the PWM line and - * thus cannot provide the program the PWM controller - * exactly. Instead, I'm relying on the fact that the - * Bootloader (u-boot or WinCE+haret) has programmed the PWM - * function group already. So I'll just modify the PWM sample - * register to follow the ratio of duty_ns vs. period_ns - * accordingly. - * - * This is good enough for programming the brightness of - * the LCD backlight. - * - * The real implementation would divide PERCLK[0] first by - * both the prescaler (/1 .. /128) and then by CLKSEL - * (/2 .. /16). - */ - u32 max = readl(imx->mmio_base + MX1_PWMP); - u32 p = max * duty_ns / period_ns; - writel(max - p, imx->mmio_base + MX1_PWMS); - } else { - BUG(); - } + /* The PWM subsystem allows for exact frequencies. However, + * I cannot connect a scope on my device to the PWM line and + * thus cannot provide the program the PWM controller + * exactly. Instead, I'm relying on the fact that the + * Bootloader (u-boot or WinCE+haret) has programmed the PWM + * function group already. So I'll just modify the PWM sample + * register to follow the ratio of duty_ns vs. period_ns + * accordingly. + * + * This is good enough for programming the brightness of + * the LCD backlight. + * + * The real implementation would divide PERCLK[0] first by + * both the prescaler (/1 .. /128) and then by CLKSEL + * (/2 .. /16). + */ + u32 max = readl(imx->mmio_base + MX1_PWMP); + u32 p = max * duty_ns / period_ns; + writel(max - p, imx->mmio_base + MX1_PWMS); + + return 0; +} + +static int imx_pwm_config_v2(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns) +{ + struct imx_chip *imx = to_imx_chip(chip); + unsigned long long c; + unsigned long period_cycles, duty_cycles, prescale; + u32 cr; + + c = clk_get_rate(imx->clk); + c = c * period_ns; + do_div(c, 1000000000); + period_cycles = c; + + prescale = period_cycles / 0x10000 + 1; + + period_cycles /= prescale; + c = (unsigned long long)period_cycles * duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + /* + * according to imx pwm RM, the real period value should be + * PERIOD value in PWMPR plus 2. + */ + if (period_cycles > 2) + period_cycles -= 2; + else + period_cycles = 0; + + writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); + writel(period_cycles, imx->mmio_base + MX3_PWMPR); + + cr = MX3_PWMCR_PRESCALER(prescale) | + MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | + MX3_PWMCR_DBGEN | MX3_PWMCR_EN; + + if (cpu_is_mx25()) + cr |= MX3_PWMCR_CLKSRC_IPG; + else + cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; + + writel(cr, imx->mmio_base + MX3_PWMCR); return 0; } +static int imx_pwm_config(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns) +{ + struct imx_chip *imx = to_imx_chip(chip); + + return imx->config(chip, pwm, duty_ns, period_ns); +} + static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct imx_chip *imx = to_imx_chip(chip); @@ -187,6 +201,11 @@ static int __devinit imx_pwm_probe(struct platform_device *pdev) if (imx->mmio_base == NULL) return -EADDRNOTAVAIL; + if (cpu_is_mx1() || cpu_is_mx21()) + imx->config = imx_pwm_config_v1; + else + imx->config = imx_pwm_config_v2; + ret = pwmchip_add(&imx->chip); if (ret < 0) return ret;