From patchwork Tue Mar 5 17:42:26 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnd Bergmann X-Patchwork-Id: 2220801 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 7C7A43FCF2 for ; Tue, 5 Mar 2013 17:43:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757500Ab3CERnW (ORCPT ); Tue, 5 Mar 2013 12:43:22 -0500 Received: from moutng.kundenserver.de ([212.227.17.9]:64833 "EHLO moutng.kundenserver.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756958Ab3CERnV (ORCPT ); Tue, 5 Mar 2013 12:43:21 -0500 Received: from wuerfel.lan (HSI-KBW-46-223-90-92.hsi.kabel-badenwuerttemberg.de [46.223.90.92]) by mrelayeu.kundenserver.de (node=mrbap4) with ESMTP (Nemesis) id 0M8hNb-1UzHDS2pL1-00vzzx; Tue, 05 Mar 2013 18:42:37 +0100 From: Arnd Bergmann To: Thomas Abraham Cc: Kukjin Kim , Tushar Behera , Deepak Saxena , linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, Olof Johansson , Arnd Bergmann Subject: [PATCH 16/23] pwm: samsung: repair the worst MMIO abuses Date: Tue, 5 Mar 2013 18:42:26 +0100 Message-Id: <1362505353-8873-17-git-send-email-arnd@arndb.de> X-Mailer: git-send-email 1.8.1.2 In-Reply-To: <1362505353-8873-1-git-send-email-arnd@arndb.de> References: <1362505353-8873-1-git-send-email-arnd@arndb.de> X-Provags-ID: V02:K0:PDmkkiP0Y2SHGNTFMFbpRmPfE5B0URCtrAT6UAXOFmD Nah0260R8G+ib80fMqna9gS48M90zo2Wu2ogcZ6+ynVsCGerCb GQxp0SwHDzNf91WhO9j9Dtg21INo/Gc8hJf5F5gm8qBBwhAnDE aVY47w94P7Dg6gOXT7pEcrS0Zkaslg2Ir+09J7OhTLfy3bCbm4 hL5kLHkAE91pHCqfZK7h/c+V52TjBBQANhpqBmBsSktV0Y50T7 3TRDk6N4ESdRts5WOobKmmaQy35GfIobQxMnMCVUWEzljNunnH KcWhtms9ThDPMMzmT+KpVSut9uYGGVuE97tomXr7tMKX5VU7i6 xlHkegSV/RPWexwneRgFLYIhAPmHsQLGldetYNwsy Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org The Samsung PWM driver uses "magic" pointers that are mapped at boot time to point its MMIO registers. This fails horribly with a multiplatform kernel, which can not rely on platform specific header files to contain the right values, aside from this being a really bad idea in general. This changes the driver to at least pass an __iomem token around in the device structure to dereference that. Fixing the platform code is much harder, so we'll leave that until we have a DT binding for pwm-samsung, which may require other changes in this area. Since we are already touching every MMIO accessor in this driver, let's also use the proper readl_relaxed variant rather than __raw_readl. Signed-off-by: Arnd Bergmann --- drivers/pwm/pwm-samsung.c | 60 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 5207e6c..9d7234d 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -22,9 +22,25 @@ #include #include -#include +#ifndef CONFIG_ARCH_MULTIPLATFORM +/* + * This is gross: the platform maps the timer at a fixed + * virtual address and expects us to use that address + * rather than a proper resource. This should get done properly + * when we get a DT binding for this driver. + */ +#include +static inline void dummy(void) +{ + BUILD_BUG_ON(S3C_VA_TIMER != IOMEM(0xf6300000)); +} +#else +#define S3C_VA_TIMER IOMEM(0xf6300000); +#endif -#include +#define S3C2410_TCON 8 +#define S3C2410_TCNTB(tmr) (0x0c + (tmr)*0x0c + 0x00) +#define S3C2410_TCMPB(tmr) (0x0c + (tmr)*0x0c + 0x04) struct s3c_chip { struct platform_device *pdev; @@ -38,6 +54,7 @@ struct s3c_chip { unsigned char tcon_base; unsigned char pwm_id; + void __iomem *base; struct pwm_chip chip; }; @@ -65,9 +82,9 @@ static int s3c_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) local_irq_save(flags); - tcon = __raw_readl(S3C2410_TCON); + tcon = readl_relaxed(s3c->base + S3C2410_TCON); tcon |= pwm_tcon_start(s3c); - __raw_writel(tcon, S3C2410_TCON); + writel_relaxed(tcon, s3c->base + S3C2410_TCON); local_irq_restore(flags); @@ -82,9 +99,9 @@ static void s3c_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) local_irq_save(flags); - tcon = __raw_readl(S3C2410_TCON); + tcon = readl_relaxed(s3c->base + S3C2410_TCON); tcon &= ~pwm_tcon_start(s3c); - __raw_writel(tcon, S3C2410_TCON); + writel_relaxed(tcon, s3c->base + S3C2410_TCON); local_irq_restore(flags); } @@ -133,8 +150,8 @@ static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, /* The TCMP and TCNT can be read without a lock, they're not * shared between the timers. */ - tcmp = __raw_readl(S3C2410_TCMPB(s3c->pwm_id)); - tcnt = __raw_readl(S3C2410_TCNTB(s3c->pwm_id)); + tcmp = readl_relaxed(s3c->base + S3C2410_TCMPB(s3c->pwm_id)); + tcnt = readl_relaxed(s3c->base + S3C2410_TCNTB(s3c->pwm_id)); period = NS_IN_HZ / period_ns; @@ -177,16 +194,16 @@ static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, local_irq_save(flags); - __raw_writel(tcmp, S3C2410_TCMPB(s3c->pwm_id)); - __raw_writel(tcnt, S3C2410_TCNTB(s3c->pwm_id)); + writel_relaxed(tcmp, s3c->base + S3C2410_TCMPB(s3c->pwm_id)); + writel_relaxed(tcnt, s3c->base + S3C2410_TCNTB(s3c->pwm_id)); - tcon = __raw_readl(S3C2410_TCON); + tcon = readl_relaxed(s3c->base + S3C2410_TCON); tcon |= pwm_tcon_manulupdate(s3c); tcon |= pwm_tcon_autoreload(s3c); - __raw_writel(tcon, S3C2410_TCON); + writel_relaxed(tcon, s3c->base + S3C2410_TCON); tcon &= ~pwm_tcon_manulupdate(s3c); - __raw_writel(tcon, S3C2410_TCON); + writel_relaxed(tcon, s3c->base + S3C2410_TCON); local_irq_restore(flags); @@ -207,6 +224,7 @@ static int s3c_pwm_probe(struct platform_device *pdev) unsigned long flags; unsigned long tcon; unsigned int id = pdev->id; + struct resource *res; int ret; if (id == 4) { @@ -220,6 +238,12 @@ static int s3c_pwm_probe(struct platform_device *pdev) return -ENOMEM; } + /* try to get a proper base address, fall back to S3C_VA_TIMER */ + s3c->base = S3C_VA_TIMER; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + s3c->base = devm_ioremap(dev, res->start, resource_size(res)); + /* calculate base of control bits in TCON */ s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4; s3c->pwm_id = id; @@ -245,9 +269,9 @@ static int s3c_pwm_probe(struct platform_device *pdev) local_irq_save(flags); - tcon = __raw_readl(S3C2410_TCON); + tcon = readl_relaxed(s3c->base + S3C2410_TCON); tcon |= pwm_tcon_invert(s3c); - __raw_writel(tcon, S3C2410_TCON); + writel_relaxed(tcon, s3c->base + S3C2410_TCON); local_irq_restore(flags); @@ -258,7 +282,7 @@ static int s3c_pwm_probe(struct platform_device *pdev) } pwm_dbg(s3c, "config bits %02x\n", - (__raw_readl(S3C2410_TCON) >> s3c->tcon_base) & 0x0f); + (readl_relaxed(s3c->base + S3C2410_TCON) >> s3c->tcon_base) & 0x0f); dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n", clk_get_rate(s3c->clk), @@ -310,9 +334,9 @@ static int s3c_pwm_resume(struct platform_device *pdev) unsigned long tcon; /* Restore invertion */ - tcon = __raw_readl(S3C2410_TCON); + tcon = readl_relaxed(s3c->base + S3C2410_TCON); tcon |= pwm_tcon_invert(s3c); - __raw_writel(tcon, S3C2410_TCON); + writel_relaxed(tcon, s3c->base + S3C2410_TCON); return 0; }