From patchwork Fri May 16 05:11:08 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Liu Ying X-Patchwork-Id: 4187641 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 5C6F2BFF02 for ; Fri, 16 May 2014 05:11:35 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 432C0201F9 for ; Fri, 16 May 2014 05:11:34 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 15D762012D for ; Fri, 16 May 2014 05:11:33 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WlAOR-0002q0-0Z; Fri, 16 May 2014 05:09:15 +0000 Received: from mail-bn1blp0181.outbound.protection.outlook.com ([207.46.163.181] helo=na01-bn1-obe.outbound.protection.outlook.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WlAOJ-0002E3-Rv for linux-arm-kernel@lists.infradead.org; Fri, 16 May 2014 05:09:09 +0000 Received: from CH1PR03CA008.namprd03.prod.outlook.com (10.255.156.153) by BL2PR03MB339.namprd03.prod.outlook.com (10.141.68.23) with Microsoft SMTP Server (TLS) id 15.0.944.11; Fri, 16 May 2014 05:08:44 +0000 Received: from BY2FFO11FD039.protection.gbl (10.255.156.132) by CH1PR03CA008.outlook.office365.com (10.255.156.153) with Microsoft SMTP Server (TLS) id 15.0.944.11 via Frontend Transport; Fri, 16 May 2014 05:08:43 +0000 Received: from az84smr01.freescale.net (192.88.158.2) by BY2FFO11FD039.mail.protection.outlook.com (10.1.14.224) with Microsoft SMTP Server (TLS) id 15.0.939.9 via Frontend Transport; Fri, 16 May 2014 05:08:43 +0000 Received: from victor.ap.freescale.net (victor.ap.freescale.net [10.192.241.62]) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id s4G58WGV032557; Thu, 15 May 2014 22:08:35 -0700 From: Liu Ying To: Subject: [PATCH v3] pwm: i.MX: Avoid sample FIFO overflow for i.MX PWM version2 Date: Fri, 16 May 2014 13:11:08 +0800 Message-ID: <1400217068-22642-1-git-send-email-Ying.Liu@freescale.com> X-Mailer: git-send-email 1.7.9.5 MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-Forefront-Antispam-Report: CIP:192.88.158.2; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(6009001)(199002)(189002)(47776003)(20776003)(80022001)(64706001)(6806004)(79102001)(84676001)(31966008)(74662001)(74502001)(77156001)(36756003)(21056001)(50466002)(23676002)(81542001)(4396001)(50226001)(81342001)(68736004)(69596002)(62966002)(19580395003)(44976005)(19580405001)(83322001)(76482001)(88136002)(81156002)(87936001)(77982001)(89996001)(93916002)(86362001)(575784001)(99396002)(50986999)(83072002)(85852003)(77096999)(87286001)(92726001)(46102001)(92566001)(97736001)(102836001); DIR:OUT; SFP:; SCL:1; SRVR:BL2PR03MB339; H:az84smr01.freescale.net; FPR:; MLV:sfv; PTR:InfoDomainNonexistent; A:1; MX:1; LANG:en; X-Forefront-PRVS: 02135EB356 Received-SPF: Fail (: domain of freescale.com does not designate 192.88.158.2 as permitted sender) receiver=; client-ip=192.88.158.2; helo=az84smr01.freescale.net; Authentication-Results: spf=fail (sender IP is 192.88.158.2) smtp.mailfrom=Ying.Liu@freescale.com; X-OriginatorOrg: freescale.com X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140515_220908_212998_C7D65A90 X-CRM114-Status: GOOD ( 20.14 ) X-Spam-Score: -0.0 (/) Cc: s.hauer@pengutronix.de, linux-kernel@vger.kernel.org, thierry.reding@gmail.com, shawn.guo@freescale.com, linux-arm-kernel@lists.infradead.org, LW@KARO-electronics.de X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.5 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The i.MX PWM version2 is embedded in several i.MX SoCs, such as i.MX27, i.MX51 and i.MX6SL. There are four 16bit sample FIFOs in this IP, each of which determines the duty period of a PWM waveform in one full cycle. The IP spec mentions that we should not write a fourth sample because the FIFOs will become full and trigger a FIFO write error (FWE) which will prevent the PWM from starting once it is enabled. In order to avoid any sample FIFO overflow issue, this patch clears all sample FIFOs or waits for a rollover event to consume a FIFO slot when necessary. In this way, only the first FIFO slot will be loaded at most. Note that the PWM controller will not generate any rollover event if the duty period is zero. This makes the logic a bit complicated to determine if we clear the sample FIFOs or wait for a rollover event. The FIFO overflow issue can be reproduced by the following commands on the i.MX6SL EVK platform, assuming we use PWM2 for the debug LED which is driven by the pin HSIC_STROBE and the maximal brightness is 255. echo 0 > /sys/class/leds/user/brightness echo 0 > /sys/class/leds/user/brightness echo 0 > /sys/class/leds/user/brightness echo 0 > /sys/class/leds/user/brightness echo 255 > /sys/class/leds/user/brightness Here, FWE happens(PWMSR register reads 0x58) and the LED can not be lighten. Cc: Thierry Reding Cc: Sascha Hauer Cc: Shawn Guo Cc: Lothar Waßmann Cc: linux-pwm@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Liu Ying --- v2->v3: * Wait for a rollover event before configuration when PWM is active with non-zero duty period. And, update commit message for that. * Fix some typos in commit head and message(fifo -> FIFO, pwm -> PWM, etc). * Cc linux-kernel@vger.kernel.org. v1->v2: * To address Lothar Waßmann's comment, add a timeout mechanism instead of endless polling the SWR bit to be cleared by the hardware. drivers/pwm/pwm-imx.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index d797c7b..204f4a9 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -30,6 +32,7 @@ /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ #define MX3_PWMCR 0x00 /* PWM Control Register */ +#define MX3_PWMIR 0x08 /* PWM Interrupt Register */ #define MX3_PWMSAR 0x0C /* PWM Sample Register */ #define MX3_PWMPR 0x10 /* PWM Period Register */ #define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4) @@ -38,7 +41,12 @@ #define MX3_PWMCR_DBGEN (1 << 22) #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) #define MX3_PWMCR_CLKSRC_IPG (1 << 16) +#define MX3_PWMCR_SWR (1 << 3) #define MX3_PWMCR_EN (1 << 0) +#define MX3_PWMSR_ROV (1 << 4) +#define MX3_PWMIR_RIE (1 << 1) + +#define MX3_PWM_SWR_LOOP 5 struct imx_chip { struct clk *clk_per; @@ -48,6 +56,9 @@ struct imx_chip { struct pwm_chip chip; + unsigned int irq; + struct completion rov_complete; + int (*config)(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns); void (*set_enable)(struct pwm_chip *chip, bool enable); @@ -99,12 +110,33 @@ static void imx_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) writel(val, imx->mmio_base + MX1_PWMC); } +/* Software reset clears all sample FIFOs. */ +static void imx_pwm_software_reset_v2(struct pwm_chip *chip) +{ + struct imx_chip *imx = to_imx_chip(chip); + struct device *dev = chip->dev; + int wait_count = 0; + u32 cr; + + writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR); + do { + usleep_range(200, 1000); + cr = readl(imx->mmio_base + MX3_PWMCR); + } while ((cr & MX3_PWMCR_SWR) && + (wait_count++ < MX3_PWM_SWR_LOOP)); + + if (cr & MX3_PWMCR_SWR) + dev_warn(dev, "software reset timeout\n"); +} + 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); + struct device *dev = chip->dev; unsigned long long c; unsigned long period_cycles, duty_cycles, prescale; + bool enable = test_bit(PWMF_ENABLED, &pwm->flags); u32 cr; c = clk_get_rate(imx->clk_per); @@ -128,6 +160,13 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, else period_cycles = 0; + if (!enable || duty_cycles == 0) + imx_pwm_software_reset_v2(chip); + else if (readl(imx->mmio_base + MX3_PWMSAR)) + /* No rollover irq generated if duty peroid is zero. */ + if (!wait_for_completion_timeout(&imx->rov_complete, HZ)) + dev_warn(dev, "timeout when waiting for rollover irq\n"); + writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); writel(period_cycles, imx->mmio_base + MX3_PWMPR); @@ -135,27 +174,55 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; - if (test_bit(PWMF_ENABLED, &pwm->flags)) + if (enable) cr |= MX3_PWMCR_EN; writel(cr, imx->mmio_base + MX3_PWMCR); + if (enable && duty_cycles) + /* No rollover irq generated if duty peroid is zero. */ + writel(MX3_PWMIR_RIE, imx->mmio_base + MX3_PWMIR); + return 0; } static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) { struct imx_chip *imx = to_imx_chip(chip); + bool enabled; u32 val; val = readl(imx->mmio_base + MX3_PWMCR); + enabled = val & MX3_PWMCR_EN; + if (enable) val |= MX3_PWMCR_EN; else val &= ~MX3_PWMCR_EN; writel(val, imx->mmio_base + MX3_PWMCR); + + if (!enable) + imx_pwm_software_reset_v2(chip); + else if (!enabled && readl(imx->mmio_base + MX3_PWMSAR)) + /* No rollover irq generated if duty period is zero. */ + writel(MX3_PWMIR_RIE, imx->mmio_base + MX3_PWMIR); +} + +static irqreturn_t imx_pwm_irq_handler_v2(int irq, void *data) +{ + struct imx_chip *imx = data; + u32 val; + + /* disable rollover interrupt */ + val = readl(imx->mmio_base + MX3_PWMIR); + val &= ~MX3_PWMIR_RIE; + writel(val, imx->mmio_base + MX3_PWMIR); + + complete(&imx->rov_complete); + + return IRQ_HANDLED; } static int imx_pwm_config(struct pwm_chip *chip, @@ -209,16 +276,19 @@ struct imx_pwm_data { int (*config)(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns); void (*set_enable)(struct pwm_chip *chip, bool enable); + irqreturn_t (*irq_handler)(int irq, void *data); }; static struct imx_pwm_data imx_pwm_data_v1 = { .config = imx_pwm_config_v1, .set_enable = imx_pwm_set_enable_v1, + .irq_handler = NULL, }; static struct imx_pwm_data imx_pwm_data_v2 = { .config = imx_pwm_config_v2, .set_enable = imx_pwm_set_enable_v2, + .irq_handler = imx_pwm_irq_handler_v2, }; static const struct of_device_id imx_pwm_dt_ids[] = { @@ -272,6 +342,23 @@ static int imx_pwm_probe(struct platform_device *pdev) imx->config = data->config; imx->set_enable = data->set_enable; + init_completion(&imx->rov_complete); + + imx->irq = platform_get_irq(pdev, 0); + if (imx->irq < 0) { + dev_err(&pdev->dev, "failed to get irq\n"); + return imx->irq; + } + + if (data->irq_handler) { + ret = devm_request_irq(&pdev->dev, imx->irq, data->irq_handler, + 0, "imx-pwm", imx); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq\n"); + return ret; + } + } + ret = pwmchip_add(&imx->chip); if (ret < 0) return ret;