From patchwork Thu Mar 26 16:39:14 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anand Moon X-Patchwork-Id: 6101111 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id B0DB39F318 for ; Thu, 26 Mar 2015 16:54:02 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C715E20411 for ; Thu, 26 Mar 2015 16:54:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 03D49203A4 for ; Thu, 26 Mar 2015 16:53:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751840AbbCZQxp (ORCPT ); Thu, 26 Mar 2015 12:53:45 -0400 Received: from mail-pa0-f67.google.com ([209.85.220.67]:35499 "EHLO mail-pa0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751220AbbCZQxo (ORCPT ); Thu, 26 Mar 2015 12:53:44 -0400 Received: by paceu11 with SMTP id eu11so15440937pac.2; Thu, 26 Mar 2015 09:53:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Z2qx9shl8OvXAZfay0/scH/jut4Umex+NQB/aVd5J+g=; b=NbdwbzyKsWw1r+kIqrceF44F6QGHslC7HBEwXcxbcRVOcVNImYY4oihsOJS7X1lPAK M756RtT4/R0G2E01clvZGU2LV4erkcKZkQqqx/hy03VOCuLsmBjTOauJ9MX+xj2FNN0a KpdZd01+tZYntbo8hafPqrDPfoW/TFtlubycR4Zq472J+TW8BsMy3WXvqq4y9FOhtFSG 5F1LXbFi7Y6Pg+qVto9P/jvvDOiv1rFQhL6TiHDYS/tweBHXWbUo57izs83pWht7dPte LJXkqRFg6FF0o3wEICju7N8kI8YBDqowphB3KlpOPW80clzaHW8igVf1i3ENYtbh77Av 8ONg== X-Received: by 10.68.182.132 with SMTP id ee4mr28544140pbc.24.1427388042078; Thu, 26 Mar 2015 09:40:42 -0700 (PDT) Received: from odroidxu3.localdomain ([103.249.89.113]) by mx.google.com with ESMTPSA id k9sm6113468pdp.24.2015.03.26.09.40.36 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 26 Mar 2015 09:40:41 -0700 (PDT) From: Anand Moon To: Lukasz Majewski , Eduardo Valentin , Sjoerd Simons , Russell King , Kukjin Kim Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, Anand Moon Subject: [PATCH 5/6] pwm: samsung: Fix output race on disabling Date: Fri, 27 Mar 2015 03:09:14 +1030 Message-Id: <1427387955-5129-6-git-send-email-linux.amoon@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1427387955-5129-1-git-send-email-linux.amoon@gmail.com> References: <1427387955-5129-1-git-send-email-linux.amoon@gmail.com> Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-5.5 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,FREEMAIL_FROM,RCVD_IN_BL_SPAMCOP_NET,RCVD_IN_DNSWL_HI, T_DKIM_INVALID, T_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 From: Sjoerd Simons When disabling the samsung PWM the output state remains at the level it was in the end of a pwm cycle. In other words, calling pwm_disable when at 100% duty will keep the output active, while at all other setting the output will go/stay inactive. On top of that the samsung PWM settings are double-buffered, which means the new settings only get applied at the start of a new PWM cycle. This results in a race if the PWM is at 100% duty and a driver calls: pwm_config (pwm, 0, period); pwm_disable (pwm); In this case the PWMs output will unexpectedly stay active, unless a new PWM cycle happened to start between the register writes in _config and _disable. As far as i can tell this is a regression introduced by 3bdf878, before that a call to pwm_config would call pwm_samsung_enable which, while heavy-handed, made sure the expected settings were live. To resolve this, while not re-introducing the issues 3bdf878 (flickering as the PWM got reset while in a PWM cycle). Only force an update of the settings when at 100% duty, which shouldn't have a noticeable effect on the output but is enough to ensure the behaviour is as expected on disable. Signed-off-by: Sjoerd Simons Signed-off-by: Anand Moon Acked-by: Lukasz Majewski --- drivers/pwm/pwm-samsung.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 3e9b583..649f6c4 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -269,12 +269,31 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) spin_unlock_irqrestore(&samsung_pwm_lock, flags); } +static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, + struct pwm_device *pwm) +{ + unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); + u32 tcon; + unsigned long flags; + + spin_lock_irqsave(&samsung_pwm_lock, flags); + + tcon = readl(chip->base + REG_TCON); + tcon |= TCON_MANUALUPDATE(tcon_chan); + writel(tcon, chip->base + REG_TCON); + + tcon &= ~TCON_MANUALUPDATE(tcon_chan); + writel(tcon, chip->base + REG_TCON); + + spin_unlock_irqrestore(&samsung_pwm_lock, flags); +} + static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); - u32 tin_ns = chan->tin_ns, tcnt, tcmp; + u32 tin_ns = chan->tin_ns, tcnt, tcmp, oldtcmp; /* * We currently avoid using 64bit arithmetic by using the @@ -288,6 +307,7 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, return 0; tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm)); + oldtcmp = readl(our_chip->base + REG_TCMPB(pwm->hwpwm)); /* We need tick count for calculation, not last tick. */ ++tcnt; @@ -335,6 +355,15 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, writel(tcnt, our_chip->base + REG_TCNTB(pwm->hwpwm)); writel(tcmp, our_chip->base + REG_TCMPB(pwm->hwpwm)); + /* In case the PWM is currently at 100% duty, force a manual update + * to prevent the signal staying high in the pwm is disabled shortly + * afer this update (before it autoreloaded the new values) . + */ + if (oldtcmp == (u32) -1) { + dev_dbg(our_chip->chip.dev, "Forcing manual update"); + pwm_samsung_manual_update(our_chip, pwm); + } + chan->period_ns = period_ns; chan->tin_ns = tin_ns; chan->duty_ns = duty_ns;