pwm: add driver for nexell

Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- Since drivers/pwm/pwm-nexell.c is an adapted version of
  s5p-common/pwm.c an appropriately changed version of s5p-common/pwm.c
  is used instead. Therefore arch/arm/mach-s5pc1xx/include/mach/pwm.h
  copied to arch/arm/mach-nexell/include/mach and s5p-common/Makefile
  changed appropriately.
- '#ifdef CONFIG...' changed to 'if (IS_ENABLED(CONFIG...))' where
  possible (and similar).

Signed-off-by: Stefan Bosch <stefan_b@posteo.net>
diff --git a/arch/arm/cpu/armv7/s5p-common/Makefile b/arch/arm/cpu/armv7/s5p-common/Makefile
index 12cf804..bfe0238 100644
--- a/arch/arm/cpu/armv7/s5p-common/Makefile
+++ b/arch/arm/cpu/armv7/s5p-common/Makefile
@@ -3,9 +3,14 @@
 # Copyright (C) 2009 Samsung Electronics
 # Minkyu Kang <mk7.kang@samsung.com>
 
-obj-y		+= cpu_info.o
+ifdef CONFIG_ARCH_NEXELL
+obj-$(CONFIG_PWM_NX) += pwm.o
+obj-$(CONFIG_S5P4418_ONEWIRE) += pwm.o
+else
+obj-y += cpu_info.o
 ifndef CONFIG_SPL_BUILD
-obj-y		+= timer.o
-obj-y		+= sromc.o
-obj-$(CONFIG_PWM)	+= pwm.o
+obj-y += timer.o
+obj-y += sromc.o
+obj-$(CONFIG_PWM) += pwm.o
+endif
 endif
diff --git a/arch/arm/cpu/armv7/s5p-common/pwm.c b/arch/arm/cpu/armv7/s5p-common/pwm.c
index 6b9e865..aef2e55 100644
--- a/arch/arm/cpu/armv7/s5p-common/pwm.c
+++ b/arch/arm/cpu/armv7/s5p-common/pwm.c
@@ -15,7 +15,11 @@
 int pwm_enable(int pwm_id)
 {
 	const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
+			(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
 			(struct s5p_timer *)samsung_get_base_timer();
+#endif
 	unsigned long tcon;
 
 	tcon = readl(&pwm->tcon);
@@ -29,7 +33,11 @@
 void pwm_disable(int pwm_id)
 {
 	const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
+			(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
 			(struct s5p_timer *)samsung_get_base_timer();
+#endif
 	unsigned long tcon;
 
 	tcon = readl(&pwm->tcon);
@@ -43,14 +51,43 @@
 	unsigned long tin_parent_rate;
 	unsigned int div;
 
+#if defined(CONFIG_ARCH_NEXELL)
+	unsigned int pre_div;
+	const struct s5p_timer *pwm =
+		(struct s5p_timer *)PHY_BASEADDR_PWM;
+	unsigned int val;
+	struct clk *clk = clk_get(CORECLK_NAME_PCLK);
+
+	tin_parent_rate = clk_get_rate(clk);
+#else
 	tin_parent_rate = get_pwm_clk();
+#endif
+
+#if defined(CONFIG_ARCH_NEXELL)
+	writel(0, &pwm->tcfg0);
+	val = readl(&pwm->tcfg0);
+
+	if (pwm_id < 2)
+		div = ((val >> 0) & 0xff) + 1;
+	else
+		div = ((val >> 8) & 0xff) + 1;
 
+	writel(0, &pwm->tcfg1);
+	val = readl(&pwm->tcfg1);
+	val = (val >> MUX_DIV_SHIFT(pwm_id)) & 0xF;
+	pre_div = (1UL << val);
+
+	freq = tin_parent_rate / div / pre_div;
+
+	return freq;
+#else
 	for (div = 2; div <= 16; div *= 2) {
 		if ((tin_parent_rate / (div << 16)) < freq)
 			return tin_parent_rate / div;
 	}
 
 	return tin_parent_rate / 16;
+#endif
 }
 
 #define NS_IN_SEC 1000000000UL
@@ -58,7 +95,11 @@
 int pwm_config(int pwm_id, int duty_ns, int period_ns)
 {
 	const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
+			(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
 			(struct s5p_timer *)samsung_get_base_timer();
+#endif
 	unsigned int offset;
 	unsigned long tin_rate;
 	unsigned long tin_ns;
@@ -84,7 +125,12 @@
 	tin_rate = pwm_calc_tin(pwm_id, frequency);
 
 	tin_ns = NS_IN_SEC / tin_rate;
-	tcnt = period_ns / tin_ns;
+
+	if (IS_ENABLED(CONFIG_ARCH_NEXELL))
+		/* The counter starts at zero. */
+		tcnt = (period_ns / tin_ns) - 1;
+	else
+		tcnt = period_ns / tin_ns;
 
 	/* Note, counters count down */
 	tcmp = duty_ns / tin_ns;
@@ -115,7 +161,11 @@
 {
 	u32 val;
 	const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
+			(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
 			(struct s5p_timer *)samsung_get_base_timer();
+#endif
 	unsigned long ticks_per_period;
 	unsigned int offset, prescaler;
 
@@ -148,7 +198,12 @@
 		ticks_per_period = -1UL;
 	} else {
 		const unsigned long pwm_hz = 1000;
+#if defined(CONFIG_ARCH_NEXELL)
+		struct clk *clk = clk_get(CORECLK_NAME_PCLK);
+		unsigned long timer_rate_hz = clk_get_rate(clk) /
+#else
 		unsigned long timer_rate_hz = get_pwm_clk() /
+#endif
 			((prescaler + 1) * (1 << div));
 
 		ticks_per_period = timer_rate_hz / pwm_hz;
diff --git a/arch/arm/mach-nexell/include/mach/pwm.h b/arch/arm/mach-nexell/include/mach/pwm.h
new file mode 100644
index 0000000..08a287d
--- /dev/null
+++ b/arch/arm/mach-nexell/include/mach/pwm.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2009 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Minkyu Kang <mk7.kang@samsung.com>
+ */
+
+#ifndef __ASM_ARM_ARCH_PWM_H_
+#define __ASM_ARM_ARCH_PWM_H_
+
+#define PRESCALER_0		(8 - 1)		/* prescaler of timer 0, 1 */
+#define PRESCALER_1		(16 - 1)	/* prescaler of timer 2, 3, 4 */
+
+/* Divider MUX */
+#define MUX_DIV_1		0		/* 1/1 period */
+#define MUX_DIV_2		1		/* 1/2 period */
+#define MUX_DIV_4		2		/* 1/4 period */
+#define MUX_DIV_8		3		/* 1/8 period */
+#define MUX_DIV_16		4		/* 1/16 period */
+
+#define MUX_DIV_SHIFT(x)	((x) * 4)
+
+#define TCON_OFFSET(x)		(((x) + 1) * (!!(x)) << 2)
+
+#define TCON_START(x)		(1 << TCON_OFFSET(x))
+#define TCON_UPDATE(x)		(1 << (TCON_OFFSET(x) + 1))
+#define TCON_INVERTER(x)	(1 << (TCON_OFFSET(x) + 2))
+#define TCON_AUTO_RELOAD(x)	(1 << (TCON_OFFSET(x) + 3))
+#define TCON4_AUTO_RELOAD	(1 << 22)
+
+#ifndef __ASSEMBLY__
+struct s5p_timer {
+	unsigned int	tcfg0;
+	unsigned int	tcfg1;
+	unsigned int	tcon;
+	unsigned int	tcntb0;
+	unsigned int	tcmpb0;
+	unsigned int	tcnto0;
+	unsigned int	tcntb1;
+	unsigned int	tcmpb1;
+	unsigned int	tcnto1;
+	unsigned int	tcntb2;
+	unsigned int	tcmpb2;
+	unsigned int	tcnto2;
+	unsigned int	tcntb3;
+	unsigned int	res1;
+	unsigned int	tcnto3;
+	unsigned int	tcntb4;
+	unsigned int	tcnto4;
+	unsigned int	tintcstat;
+};
+#endif	/* __ASSEMBLY__ */
+
+#endif