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.
Tomasz Figa has a set of patches to clean this up in a proper
way, but that might be too late for 3.10.
Signed-off-by: Arnd Bergmann a...@arndb.de
Cc: Tomasz Figa t.f...@samsung.com
Cc: Thierry Reding thierry.red...@avionic-design.de
---
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 linux/io.h
#include linux/pwm.h
-#include mach/map.h
+#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 plat/map-base.h
+static inline void dummy(void)
+{
+ BUILD_BUG_ON(S3C_VA_TIMER != IOMEM(0xf630));
+}
+#else
+#define S3C_VA_TIMER IOMEM(0xf630);
+#endif
-#include plat/regs-timer.h
+#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 chartcon_base;
unsigned charpwm_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