Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] mmc: mmci: Bugfix in pio read for small packets
From: Linus Walleij @ 2011-10-09  6:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20111008091036.GF25689@n2100.arm.linux.org.uk>

On Sat, Oct 8, 2011 at 11:10 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Fri, Oct 07, 2011 at 03:45:48PM +0200, Ulf Hansson wrote:
>
> Well, the patch system says that each one depends on the previous one,
> and the first one in the series contains the PIO read thing.
>
> Do 7108/1 onwards depend on 7106/1 and 7107/1 ?

Probably my fault, since I was helping Ulf out with the patch tracker
introduction.

The patches should be pretty much semantically orthogonal as
far as I can tell, the only reason dependency was stated that way
was that I couldn't tell from head-parsing whether there were
syntactical dependencies. (And wanted to avoid the annoyance
of non-applying patches...)

So if they apply, they are independent AFAICT.

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH 17/26] ARM: pxa: pxa95x is incompatible with earlier pxa
From: Eric Miao @ 2011-10-09  7:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAN1soZzbtqxfNLS_JVhn1irXRVThL=yt_VWfmdkoUL2ShFDEGQ@mail.gmail.com>

>> They are actually not mutually exclusive - it's a trick we use to select
>> MACH_TAVOREVB3 by default but this option also disappears once SAARB
>> is selected.
>>
> OK. Could I know why saarb should be deselected while tavorevb3 is selected?

Haojian, it's not being deselected, it's just when user selects SAARB,
this config PXA_V7_MACH_AUTO becomes invalid (which means at
least one PXA_V7_MACH is selected), and then you could select
TAVOREVB3 as well.

Just go ahead and apply the patch, make menuconfig and see how
the trick works :-)

^ permalink raw reply

* [PULL] i.MX
From: Sascha Hauer @ 2011-10-09  9:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <109850802.HMOqKGRdrl@wuerfel>

On Sat, Oct 08, 2011 at 05:14:39PM +0200, Arnd Bergmann wrote:
> Hi Sascha,
> 
> I've found a few new build errors using randconfig after pulling the
> i.MX tree, here are the patches I did to resolve them. If you agree
> with the patches, just send me an Ack and I'll add them on top.

All acked-by: Sascha Hauer <s.hauer@pengutronix.de>

Sascha


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* [PATCH 1/2] drivers: create a pin control subsystem v9
From: Shawn Guo @ 2011-10-09  9:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1317629862-13549-1-git-send-email-linus.walleij@stericsson.com>

On Mon, Oct 03, 2011 at 10:17:42AM +0200, Linus Walleij wrote:
[...]
> diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h
> new file mode 100644
> index 0000000..2cd4033
> --- /dev/null
> +++ b/include/linux/pinctrl/machine.h
> @@ -0,0 +1,107 @@
> +/*
> + * Machine interface for the pinctrl subsystem.
> + *
> + * Copyright (C) 2011 ST-Ericsson SA
> + * Written on behalf of Linaro for ST-Ericsson
> + * Based on bits of regulator core, gpio core and clk core
> + *
> + * Author: Linus Walleij <linus.walleij@linaro.org>
> + *
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +#ifndef __LINUX_PINMUX_MACHINE_H
> +#define __LINUX_PINMUX_MACHINE_H
> +
> +/**
> + * struct pinmux_map - boards/machines shall provide this map for devices
> + * @name: the name of this specific map entry for the particular machine.
> + *	This is the second parameter passed to pinmux_get() when you want
> + *	to have several mappings to the same device
> + * @ctrl_dev: the pin control device to be used by this mapping, may be NULL
> + *	if you provide .ctrl_dev_name instead (this is more common)
> + * @ctrl_dev_name: the name of the device controlling this specific mapping,
> + *	the name must be the same as in your struct device*, may be NULL if
> + *	you provide .ctrl_dev instead
> + * @function: a function in the driver to use for this mapping, the driver
> + *	will lookup the function referenced by this ID on the specified
> + *	pin control device
> + * @group: sometimes a function can map to different pin groups, so this
> + *	selects a certain specific pin group to activate for the function, if
> + *	left as NULL, the first applicable group will be used
> + * @dev: the device using this specific mapping, may be NULL if you provide
> + *	.dev_name instead (this is more common)
> + * @dev_name: the name of the device using this specific mapping, the name
> + *	must be the same as in your struct device*, may be NULL if you
> + *	provide .dev instead
> + * @hog_on_boot: if this is set to true, the regulator subsystem will itself
						^^^^^^^^^
s/regulator/pinmux?

> + *	hog the mappings as the pinmux device drivers are attched, so this is
> + *	typically used with system maps (mux mappings without an assigned
> + *	device) that you want to get hogged and enabled by default as soon as
> + *	a pinmux device supporting it is registered. These maps will not be
> + *	disabled and put until the system shuts down.
> + */
> +struct pinmux_map {
> +	const char *name;
> +	struct device *ctrl_dev;
> +	const char *ctrl_dev_name;
> +	const char *function;
> +	const char *group;
> +	struct device *dev;
> +	const char *dev_name;
> +	const bool hog_on_boot;
> +};
> +
> +/*
> + * Convenience macro to set a simple map from a certain pin controller and a
> + * certain function to a named device
> + */
> +#define PINMUX_MAP(a, b, c, d) \
> +	{ .name = a, .ctrl_dev_name = b, .function = c, .dev_name = d }
> +
> +/*
> + * Convenience macro to map a system function onto a certain pinctrl device.
> + * System functions are not assigned to a particular device.
> + */
> +#define PINMUX_MAP_SYS(a, b, c) \
> +	{ .name = a, .ctrl_dev_name = b, .function = c }
> +
> +/*
> + * Convenience macro to map a function onto the primary device pinctrl device
> + * this is especially helpful on systems that have only one pin controller
> + * or need to set up a lot of mappings on the primary controller.
> + */
> +#define PINMUX_MAP_PRIMARY(a, b, c) \
> +	{ .name = a, .ctrl_dev_name = "pinctrl.0", .function = b, \
> +	  .dev_name = c }
> +
> +/*
> + * Convenience macro to map a system function onto the primary pinctrl device.
> + * System functions are not assigned to a particular device.
> + */
> +#define PINMUX_MAP_PRIMARY_SYS(a, b) \
> +	{ .name = a, .ctrl_dev_name = "pinctrl.0", .function = b }
> +
> +/*
> + * Convenience macro to map a system function onto the primary pinctrl device,
> + * to be hogged by the pinmux core until the system shuts down.
> + */
> +#define PINMUX_MAP_PRIMARY_SYS_HOG(a, b) \
> +	{ .name = a, .ctrl_dev_name = "pinctrl.0", .function = b, \
> +	  .hog_on_boot = true }
> +
> +
> +#ifdef CONFIG_PINMUX
> +
> +extern int pinmux_register_mappings(struct pinmux_map const *map,
> +				unsigned num_maps);
> +
> +#else
> +
> +static inline int pinmux_register_mappings(struct pinmux_map const *map,
> +					   unsigned num_maps)
> +{
> +	return 0;
> +}
> +
> +#endif /* !CONFIG_PINCTRL */

s/!CONFIG_PINCTRL/CONFIG_PINMUX?

> +#endif

-- 
Regards,
Shawn

^ permalink raw reply

* [V2] ARM: mx5: use generic irq chip pm interface for pm functions on
From: Hui Wang @ 2011-10-09  9:42 UTC (permalink / raw)
  To: linux-arm-kernel

Two problems exist in the current i.MX5 pm suspend/resume and idle
functions. The first is the current i.MX5 suspend routine will call
tzic_enable_wake(1) to set wake source, this will set all enabled
irq as wake source rather than those wake capable. The second
is i.MX5 idle will call mx5_cpu_lp_set() to prepare enter low power
mode, but it forgets to call wfi instruction to enter this mode.

To fix these two problems, using generic irq chip pm interface and
modify function imx5_idle().

[Tested by Shawn Guo on imx51 babbage board.
 Tested by Hui Wang on imx51 pdk board.]

Signed-off-by: Hui Wang <jason77.wang@gmail.com>
Tested-by: Shawn Guo <shawn.guo@linaro.org>
---

In the V2:
rebase the patch on imx-cleanup branch.
change the patch subject.

 arch/arm/mach-mx5/mm.c               |   13 +++++++++++
 arch/arm/mach-mx5/system.c           |    3 --
 arch/arm/plat-mxc/include/mach/mxc.h |    2 +-
 arch/arm/plat-mxc/tzic.c             |   40 ++++++++++++++++++++++++---------
 4 files changed, 43 insertions(+), 15 deletions(-)

diff --git a/arch/arm/mach-mx5/mm.c b/arch/arm/mach-mx5/mm.c
index 26eacc9..39194ea 100644
--- a/arch/arm/mach-mx5/mm.c
+++ b/arch/arm/mach-mx5/mm.c
@@ -13,6 +13,7 @@
 
 #include <linux/mm.h>
 #include <linux/init.h>
+#include <linux/clk.h>
 
 #include <asm/mach/map.h>
 
@@ -21,9 +22,21 @@
 #include <mach/devices-common.h>
 #include <mach/iomux-v3.h>
 
+static struct clk *gpc_dvfs_clk;
+
 static void imx5_idle(void)
 {
+	if (gpc_dvfs_clk == NULL)
+		gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs");
+
+	/* gpc clock is needed for SRPG */
+	clk_enable(gpc_dvfs_clk);
 	mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
+	if (tzic_enable_wake() != 0)
+		goto exit;
+	cpu_do_idle();
+exit:
+	clk_disable(gpc_dvfs_clk);
 }
 
 /*
diff --git a/arch/arm/mach-mx5/system.c b/arch/arm/mach-mx5/system.c
index 144ebeb..5eebfaa 100644
--- a/arch/arm/mach-mx5/system.c
+++ b/arch/arm/mach-mx5/system.c
@@ -55,9 +55,6 @@ void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
 			stop_mode = 1;
 		}
 		arm_srpgcr |= MXC_SRPGCR_PCR;
-
-		if (tzic_enable_wake(1) != 0)
-			return;
 		break;
 	case STOP_POWER_ON:
 		ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h
index 00a7819..c7f556d 100644
--- a/arch/arm/plat-mxc/include/mach/mxc.h
+++ b/arch/arm/plat-mxc/include/mach/mxc.h
@@ -182,7 +182,7 @@ struct cpu_op {
 	u32 cpu_rate;
 };
 
-int tzic_enable_wake(int is_idle);
+int tzic_enable_wake(void);
 
 extern struct cpu_op *(*get_cpu_op)(int *op);
 #endif
diff --git a/arch/arm/plat-mxc/tzic.c b/arch/arm/plat-mxc/tzic.c
index f257fcc..094b882 100644
--- a/arch/arm/plat-mxc/tzic.c
+++ b/arch/arm/plat-mxc/tzic.c
@@ -72,7 +72,28 @@ static int tzic_set_irq_fiq(unsigned int irq, unsigned int type)
 #define tzic_set_irq_fiq NULL
 #endif
 
-static unsigned int *wakeup_intr[4];
+#ifdef CONFIG_PM
+static void tzic_irq_suspend(struct irq_data *d)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+	int idx = gc->irq_base >> 5;
+
+	__raw_writel(gc->wake_active, tzic_base + TZIC_WAKEUP0(idx));
+}
+
+static void tzic_irq_resume(struct irq_data *d)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+	int idx = gc->irq_base >> 5;
+
+	__raw_writel(__raw_readl(tzic_base + TZIC_ENSET0(idx)),
+		     tzic_base + TZIC_WAKEUP0(idx));
+}
+
+#else
+#define tzic_irq_suspend NULL
+#define tzic_irq_resume NULL
+#endif
 
 static __init void tzic_init_gc(unsigned int irq_start)
 {
@@ -84,12 +105,13 @@ static __init void tzic_init_gc(unsigned int irq_start)
 				    handle_level_irq);
 	gc->private = tzic_set_irq_fiq;
 	gc->wake_enabled = IRQ_MSK(32);
-	wakeup_intr[idx] = &gc->wake_active;
 
 	ct = gc->chip_types;
 	ct->chip.irq_mask = irq_gc_mask_disable_reg;
 	ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
 	ct->chip.irq_set_wake = irq_gc_set_wake;
+	ct->chip.irq_suspend = tzic_irq_suspend;
+	ct->chip.irq_resume = tzic_irq_resume;
 	ct->regs.disable = TZIC_ENCLEAR0(idx);
 	ct->regs.enable = TZIC_ENSET0(idx);
 
@@ -138,23 +160,19 @@ void __init tzic_init_irq(void __iomem *irqbase)
 /**
  * tzic_enable_wake() - enable wakeup interrupt
  *
- * @param is_idle		1 if called in idle loop (ENSET0 register);
- *				0 to be used when called from low power entry
  * @return			0 if successful; non-zero otherwise
  */
-int tzic_enable_wake(int is_idle)
+int tzic_enable_wake(void)
 {
-	unsigned int i, v;
+	unsigned int i;
 
 	__raw_writel(1, tzic_base + TZIC_DSMINT);
 	if (unlikely(__raw_readl(tzic_base + TZIC_DSMINT) == 0))
 		return -EAGAIN;
 
-	for (i = 0; i < 4; i++) {
-		v = is_idle ? __raw_readl(tzic_base + TZIC_ENSET0(i)) :
-			*wakeup_intr[i];
-		__raw_writel(v, tzic_base + TZIC_WAKEUP0(i));
-	}
+	for (i = 0; i < 4; i++)
+		__raw_writel(__raw_readl(tzic_base + TZIC_ENSET0(i)),
+			     tzic_base + TZIC_WAKEUP0(i));
 
 	return 0;
 }
-- 
1.7.6

^ permalink raw reply related

* [PATCH v3] pinmux: add a driver for the CSR SiRFprimaII pinmux
From: Barry Song @ 2011-10-09 10:11 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rongjun Ying <Rongjun.Ying@csr.com>

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 -v3:
 rebase to Linux W's v9;
 use preprocessor to shorten pin function lists;

 drivers/pinctrl/Kconfig       |    7 +
 drivers/pinctrl/Makefile      |    1 +
 drivers/pinctrl/pinmux-sirf.c | 1215 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1223 insertions(+), 0 deletions(-)
 create mode 100644 drivers/pinctrl/pinmux-sirf.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 8b3d081..98e013c 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -26,6 +26,13 @@ config DEBUG_PINCTRL
 	help
 	  Say Y here to add some extra checks and diagnostics to PINCTRL calls.
 
+config PINMUX_SIRF
+	bool "CSR SiRFprimaII pinmux driver"
+	depends on ARCH_PRIMA2
+	select PINMUX
+	help
+	  Say Y here to enable the SiRFprimaII pinmux driver
+
 config PINMUX_U300
 	bool "U300 pinmux driver"
 	depends on ARCH_U300
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 63d4241..bdc548a 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -4,4 +4,5 @@ ccflags-$(CONFIG_DEBUG_PINMUX)	+= -DDEBUG
 
 obj-$(CONFIG_PINCTRL)		+= core.o
 obj-$(CONFIG_PINMUX)		+= pinmux.o
+obj-$(CONFIG_PINMUX_SIRF)	+= pinmux-sirf.o
 obj-$(CONFIG_PINMUX_U300)	+= pinmux-u300.o
diff --git a/drivers/pinctrl/pinmux-sirf.c b/drivers/pinctrl/pinmux-sirf.c
new file mode 100644
index 0000000..de5c78a
--- /dev/null
+++ b/drivers/pinctrl/pinmux-sirf.c
@@ -0,0 +1,1215 @@
+/*
+ * pinmux driver for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/bitops.h>
+
+#define DRIVER_NAME "pinmux-sirf"
+
+#define SIRFSOC_NUM_PADS    622
+#define SIRFSOC_GPIO_PAD_EN(g) ((g)*0x100 + 0x84)
+#define SIRFSOC_RSC_PIN_MUX 0x4
+
+/*
+ * pad list for the pinmux subsystem
+ * refer to CS-131858-DC-6A.xls
+ */
+static const struct pinctrl_pin_desc __refdata sirfsoc_pads[] = {
+	PINCTRL_PIN(4, "pwm0"),
+	PINCTRL_PIN(5, "pwm1"),
+	PINCTRL_PIN(6, "pwm2"),
+	PINCTRL_PIN(7, "pwm3"),
+	PINCTRL_PIN(8, "warm_rst_b"),
+	PINCTRL_PIN(9, "odo_0"),
+	PINCTRL_PIN(10, "odo_1"),
+	PINCTRL_PIN(11, "dr_dir"),
+	PINCTRL_PIN(13, "scl_1"),
+	PINCTRL_PIN(15, "sda_1"),
+	PINCTRL_PIN(16, "x_ldd[16]"),
+	PINCTRL_PIN(17, "x_ldd[17]"),
+	PINCTRL_PIN(18, "x_ldd[18]"),
+	PINCTRL_PIN(19, "x_ldd[19]"),
+	PINCTRL_PIN(20, "x_ldd[20]"),
+	PINCTRL_PIN(21, "x_ldd[21]"),
+	PINCTRL_PIN(22, "x_ldd[22]"),
+	PINCTRL_PIN(23, "x_ldd[23], lcdrom_frdy"),
+	PINCTRL_PIN(24, "gps_sgn"),
+	PINCTRL_PIN(25, "gps_mag"),
+	PINCTRL_PIN(26, "gps_clk"),
+	PINCTRL_PIN(27,	"sd_cd_b_1"),
+	PINCTRL_PIN(28, "sd_vcc_on_1"),
+	PINCTRL_PIN(29, "sd_wp_b_1"),
+	PINCTRL_PIN(30, "sd_clk_3"),
+	PINCTRL_PIN(31, "sd_cmd_3"),
+
+	PINCTRL_PIN(32, "x_sd_dat_3[0]"),
+	PINCTRL_PIN(33, "x_sd_dat_3[1]"),
+	PINCTRL_PIN(34, "x_sd_dat_3[2]"),
+	PINCTRL_PIN(35, "x_sd_dat_3[3]"),
+	PINCTRL_PIN(36, "x_sd_clk_4"),
+	PINCTRL_PIN(37, "x_sd_cmd_4"),
+	PINCTRL_PIN(38, "x_sd_dat_4[0]"),
+	PINCTRL_PIN(39, "x_sd_dat_4[1]"),
+	PINCTRL_PIN(40, "x_sd_dat_4[2]"),
+	PINCTRL_PIN(41, "x_sd_dat_4[3]"),
+	PINCTRL_PIN(42, "x_cko_1"),
+	PINCTRL_PIN(43, "x_ac97_bit_clk"),
+	PINCTRL_PIN(44, "x_ac97_dout"),
+	PINCTRL_PIN(45, "x_ac97_din"),
+	PINCTRL_PIN(46, "x_ac97_sync"),
+	PINCTRL_PIN(47, "x_txd_1"),
+	PINCTRL_PIN(48, "x_txd_2"),
+	PINCTRL_PIN(49, "x_rxd_1"),
+	PINCTRL_PIN(50, "x_rxd_2"),
+	PINCTRL_PIN(51, "x_usclk_0"),
+	PINCTRL_PIN(52, "x_utxd_0"),
+	PINCTRL_PIN(53, "x_urxd_0"),
+	PINCTRL_PIN(54, "x_utfs_0"),
+	PINCTRL_PIN(55, "x_urfs_0"),
+	PINCTRL_PIN(56, "x_usclk_1"),
+	PINCTRL_PIN(57, "x_utxd_1"),
+	PINCTRL_PIN(58, "x_urxd_1"),
+	PINCTRL_PIN(59, "x_utfs_1"),
+	PINCTRL_PIN(60, "x_urfs_1"),
+	PINCTRL_PIN(61, "x_usclk_2"),
+	PINCTRL_PIN(62, "x_utxd_2"),
+	PINCTRL_PIN(63, "x_urxd_2"),
+
+	PINCTRL_PIN(64, "x_utfs_2"),
+	PINCTRL_PIN(65, "x_urfs_2"),
+	PINCTRL_PIN(66, "x_df_we_b"),
+	PINCTRL_PIN(67, "x_df_re_b"),
+	PINCTRL_PIN(68, "x_txd_0"),
+	PINCTRL_PIN(69, "x_rxd_0"),
+	PINCTRL_PIN(78, "x_cko_0"),
+	PINCTRL_PIN(79, "x_vip_pxd[7]"),
+	PINCTRL_PIN(80, "x_vip_pxd[6]"),
+	PINCTRL_PIN(81, "x_vip_pxd[5]"),
+	PINCTRL_PIN(82, "x_vip_pxd[4]"),
+	PINCTRL_PIN(83, "x_vip_pxd[3]"),
+	PINCTRL_PIN(84, "x_vip_pxd[2]"),
+	PINCTRL_PIN(85, "x_vip_pxd[1]"),
+	PINCTRL_PIN(86, "x_vip_pxd[0]"),
+	PINCTRL_PIN(87, "x_vip_vsync"),
+	PINCTRL_PIN(88, "x_vip_hsync"),
+	PINCTRL_PIN(89, "x_vip_pxclk"),
+	PINCTRL_PIN(90, "x_sda_0"),
+	PINCTRL_PIN(91, "x_scl_0"),
+	PINCTRL_PIN(92, "x_df_ry_by"),
+	PINCTRL_PIN(93, "x_df_cs_b[1]"),
+	PINCTRL_PIN(94, "x_df_cs_b[0]"),
+	PINCTRL_PIN(95, "x_l_pclk"),
+
+	PINCTRL_PIN(96, "x_l_lck"),
+	PINCTRL_PIN(97, "x_l_fck"),
+	PINCTRL_PIN(98, "x_l_de"),
+	PINCTRL_PIN(99, "x_ldd[0]"),
+	PINCTRL_PIN(100, "x_ldd[1]"),
+	PINCTRL_PIN(101, "x_ldd[2]"),
+	PINCTRL_PIN(102, "x_ldd[3]"),
+	PINCTRL_PIN(103, "x_ldd[4]"),
+	PINCTRL_PIN(104, "x_ldd[5]"),
+	PINCTRL_PIN(105, "x_ldd[6]"),
+	PINCTRL_PIN(106, "x_ldd[7]"),
+	PINCTRL_PIN(107, "x_ldd[8]"),
+	PINCTRL_PIN(108, "x_ldd[9]"),
+	PINCTRL_PIN(109, "x_ldd[10]"),
+	PINCTRL_PIN(110, "x_ldd[11]"),
+	PINCTRL_PIN(111, "x_ldd[12]"),
+	PINCTRL_PIN(112, "x_ldd[13]"),
+	PINCTRL_PIN(113, "x_ldd[14]"),
+	PINCTRL_PIN(114, "x_ldd[15]"),
+};
+
+/**
+ * @dev: a pointer back to containing device
+ * @virtbase: the offset to the controller in virtual memory
+ */
+struct sirfsoc_pmx {
+	struct device *dev;
+	struct pinctrl_dev *pmx;
+	void __iomem *gpio_virtbase;
+	void __iomem *rsc_virtbase;
+};
+
+/* SIRFSOC_GPIO_PAD_EN set */
+struct sirfsoc_muxmask {
+	unsigned long group;
+	unsigned long mask;
+};
+
+struct sirfsoc_padmux {
+	unsigned long muxmask_counts;
+	const struct sirfsoc_muxmask *muxmask;
+	/* RSC_PIN_MUX set */
+	unsigned long funcmask;
+	unsigned long funcval;
+};
+
+ /**
+ * struct sirfsoc_pin_group - describes a SiRFprimaII pin group
+ * @name: the name of this specific pin group
+ * @pins: an array of discrete physical pins used in this group, taken
+ *	from the driver-local pin enumeration space
+ * @num_pins: the number of pins in this group array, i.e. the number of
+ *	elements in .pins so we can iterate over that array
+ */
+struct sirfsoc_pin_group {
+	const char *name;
+	const unsigned int *pins;
+	const unsigned num_pins;
+};
+
+static const struct sirfsoc_muxmask lcd_16bits_sirfsoc_muxmask[] = {
+	{
+		.group = 3,
+		.mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) |
+			BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
+			BIT(17) | BIT(18),
+	}, {
+		.group = 2,
+		.mask = BIT(31),
+	},
+};
+
+static const struct sirfsoc_padmux lcd_16bits_padmux = {
+	.muxmask_counts = ARRAY_SIZE(lcd_16bits_sirfsoc_muxmask),
+	.muxmask = lcd_16bits_sirfsoc_muxmask,
+	.funcmask = BIT(4),
+	.funcval = 0,
+};
+
+static const unsigned lcd_16bits_pins[] = { 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+	105, 106, 107, 108, 109, 110, 111, 112, 113, 114 };
+
+static const struct sirfsoc_muxmask lcd_18bits_muxmask[] = {
+	{
+		.group = 3,
+		.mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) |
+			BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
+			BIT(17) | BIT(18),
+	}, {
+		.group = 2,
+		.mask = BIT(31),
+	}, {
+		.group = 0,
+		.mask = BIT(16) | BIT(17),
+	},
+};
+
+static const struct sirfsoc_padmux lcd_18bits_padmux = {
+	.muxmask_counts = ARRAY_SIZE(lcd_18bits_muxmask),
+	.muxmask = lcd_18bits_muxmask,
+	.funcmask = BIT(4),
+	.funcval = 0,
+};
+
+static const unsigned lcd_18bits_pins[] = { 16, 17, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+	105, 106, 107, 108, 109, 110, 111, 112, 113, 114};
+
+static const struct sirfsoc_muxmask lcd_24bits_muxmask[] = {
+	{
+		.group = 3,
+		.mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) |
+			BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
+			BIT(17) | BIT(18),
+	}, {
+		.group = 2,
+		.mask = BIT(31),
+	}, {
+		.group = 0,
+		.mask = BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23),
+	},
+};
+
+static const struct sirfsoc_padmux lcd_24bits_padmux = {
+	.muxmask_counts = ARRAY_SIZE(lcd_24bits_muxmask),
+	.muxmask = lcd_24bits_muxmask,
+	.funcmask = BIT(4),
+	.funcval = 0,
+};
+
+static const unsigned lcd_24bits_pins[] = { 16, 17, 18, 19, 20, 21, 22, 23, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+	105, 106, 107, 108, 109, 110, 111, 112, 113, 114 };
+
+static const struct sirfsoc_muxmask lcdrom_muxmask[] = {
+	{
+		.group = 3,
+		.mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) |
+			BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
+			BIT(17) | BIT(18),
+	}, {
+		.group = 2,
+		.mask = BIT(31),
+	}, {
+		.group = 0,
+		.mask = BIT(23),
+	},
+};
+
+static const struct sirfsoc_padmux lcdrom_padmux = {
+	.muxmask_counts = ARRAY_SIZE(lcdrom_muxmask),
+	.muxmask = lcdrom_muxmask,
+	.funcmask = BIT(4),
+	.funcval = BIT(4),
+};
+
+static const unsigned lcdrom_pins[] = { 23, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+	105, 106, 107, 108, 109, 110, 111, 112, 113, 114 };
+
+static const struct sirfsoc_muxmask uart0_muxmask[] = {
+	{
+		.group = 2,
+		.mask = BIT(4) | BIT(5),
+	}, {
+		.group = 1,
+		.mask = BIT(23) | BIT(28),
+	},
+};
+
+static const struct sirfsoc_padmux uart0_padmux = {
+	.muxmask_counts = ARRAY_SIZE(uart0_muxmask),
+	.muxmask = uart0_muxmask,
+	.funcmask = BIT(9),
+	.funcval = BIT(9),
+};
+
+static const unsigned uart0_pins[] = { 55, 60, 68, 69 };
+
+static const struct sirfsoc_muxmask uart0_nostreamctrl_muxmask[] = {
+	{
+		.group = 2,
+		.mask = BIT(4) | BIT(5),
+	},
+};
+
+static const struct sirfsoc_padmux uart0_nostreamctrl_padmux = {
+	.muxmask_counts = ARRAY_SIZE(uart0_nostreamctrl_muxmask),
+	.muxmask = uart0_nostreamctrl_muxmask,
+};
+
+static const unsigned uart0_nostreamctrl_pins[] = { 68, 39 };
+
+static const struct sirfsoc_muxmask uart1_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(15) | BIT(17),
+	},
+};
+
+static const struct sirfsoc_padmux uart1_padmux = {
+	.muxmask_counts = ARRAY_SIZE(uart1_muxmask),
+	.muxmask = uart1_muxmask,
+};
+
+static const unsigned uart1_pins[] = { 47, 49 };
+
+static const struct sirfsoc_muxmask uart2_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(16) | BIT(18) | BIT(24) | BIT(27),
+	},
+};
+
+static const struct sirfsoc_padmux uart2_padmux = {
+	.muxmask_counts = ARRAY_SIZE(uart2_muxmask),
+	.muxmask = uart2_muxmask,
+	.funcmask = BIT(10),
+	.funcval = BIT(10),
+};
+
+static const unsigned uart2_pins[] = { 48, 50, 56, 59 };
+
+static const struct sirfsoc_muxmask uart2_nostreamctrl_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(16) | BIT(18),
+	},
+};
+
+static const struct sirfsoc_padmux uart2_nostreamctrl_padmux = {
+	.muxmask_counts = ARRAY_SIZE(uart2_nostreamctrl_muxmask),
+	.muxmask = uart2_nostreamctrl_muxmask,
+};
+
+static const unsigned uart2_nostreamctrl_pins[] = { 48, 50 };
+
+static const struct sirfsoc_muxmask sdmmc3_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(30) | BIT(31),
+	}, {
+		.group = 1,
+		.mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
+	},
+};
+
+static const struct sirfsoc_padmux sdmmc3_padmux = {
+	.muxmask_counts = ARRAY_SIZE(sdmmc3_muxmask),
+	.muxmask = sdmmc3_muxmask,
+	.funcmask = BIT(7),
+	.funcval = 0,
+};
+
+static const unsigned sdmmc3_pins[] = { 30, 31, 32, 33, 34, 35 };
+
+static const struct sirfsoc_muxmask spi0_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(0) | BIT(1) | BIT(2) | BIT(3),
+	},
+};
+
+static const struct sirfsoc_padmux spi0_padmux = {
+	.muxmask_counts = ARRAY_SIZE(spi0_muxmask),
+	.muxmask = spi0_muxmask,
+	.funcmask = BIT(7),
+	.funcval = BIT(7),
+};
+
+static const unsigned spi0_pins[] = { 32, 33, 34, 35 };
+
+static const struct sirfsoc_muxmask sdmmc4_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9),
+	},
+};
+
+static const struct sirfsoc_padmux sdmmc4_padmux = {
+	.muxmask_counts = ARRAY_SIZE(sdmmc4_muxmask),
+	.muxmask = sdmmc4_muxmask,
+};
+
+static const unsigned sdmmc4_pins[] = { 36, 37, 38, 39, 40, 41 };
+
+static const struct sirfsoc_muxmask cko1_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(10),
+	},
+};
+
+static const struct sirfsoc_padmux cko1_padmux = {
+	.muxmask_counts = ARRAY_SIZE(cko1_muxmask),
+	.muxmask = cko1_muxmask,
+	.funcmask = BIT(3),
+	.funcval = 0,
+};
+
+static const unsigned cko1_pins[] = { 42 };
+
+static const struct sirfsoc_muxmask i2s_muxmask[] = {
+	{
+		.group = 1,
+		.mask =
+			BIT(10) | BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(19)
+				| BIT(23) | BIT(28),
+	},
+};
+
+static const struct sirfsoc_padmux i2s_padmux = {
+	.muxmask_counts = ARRAY_SIZE(i2s_muxmask),
+	.muxmask = i2s_muxmask,
+	.funcmask = BIT(3) | BIT(9),
+	.funcval = BIT(3),
+};
+
+static const unsigned i2s_pins[] = { 42, 43, 44, 45, 46, 51, 55, 60 };
+
+static const struct sirfsoc_muxmask ac97_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(11) | BIT(12) | BIT(13) | BIT(14),
+	},
+};
+
+static const struct sirfsoc_padmux ac97_padmux = {
+	.muxmask_counts = ARRAY_SIZE(ac97_muxmask),
+	.muxmask = ac97_muxmask,
+	.funcmask = BIT(8),
+	.funcval = 0,
+};
+
+static const unsigned ac97_pins[] = { 33, 34, 35, 36 };
+
+static const struct sirfsoc_muxmask spi1_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(11) | BIT(12) | BIT(13) | BIT(14),
+	},
+};
+
+static const struct sirfsoc_padmux spi1_padmux = {
+	.muxmask_counts = ARRAY_SIZE(spi1_muxmask),
+	.muxmask = spi1_muxmask,
+	.funcmask = BIT(8),
+	.funcval = BIT(8),
+};
+
+static const unsigned spi1_pins[] = { 33, 34, 35, 36 };
+
+static const struct sirfsoc_muxmask sdmmc1_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(27) | BIT(28) | BIT(29),
+	},
+};
+
+static const struct sirfsoc_padmux sdmmc1_padmux = {
+	.muxmask_counts = ARRAY_SIZE(sdmmc1_muxmask),
+	.muxmask = sdmmc1_muxmask,
+};
+
+static const unsigned sdmmc1_pins[] = { 27, 28, 29 };
+
+static const struct sirfsoc_muxmask gps_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(24) | BIT(25) | BIT(26),
+	},
+};
+
+static const struct sirfsoc_padmux gps_padmux = {
+	.muxmask_counts = ARRAY_SIZE(gps_muxmask),
+	.muxmask = gps_muxmask,
+	.funcmask = BIT(12) | BIT(13) | BIT(14),
+	.funcval = BIT(12),
+};
+
+static const unsigned gps_pins[] = { 24, 25, 26 };
+
+static const struct sirfsoc_muxmask sdmmc5_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(24) | BIT(25) | BIT(26),
+	}, {
+		.group = 1,
+		.mask = BIT(29),
+	}, {
+		.group = 2,
+		.mask = BIT(0) | BIT(1),
+	},
+};
+
+static const struct sirfsoc_padmux sdmmc5_padmux = {
+	.muxmask_counts = ARRAY_SIZE(sdmmc5_muxmask),
+	.muxmask = sdmmc5_muxmask,
+	.funcmask = BIT(13) | BIT(14),
+	.funcval = BIT(13) | BIT(14),
+};
+
+static const unsigned sdmmc5_pins[] = { 24, 25, 26, 61, 64, 65 };
+
+static const struct sirfsoc_muxmask usp0_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23),
+	},
+};
+
+static const struct sirfsoc_padmux usp0_padmux = {
+	.muxmask_counts = ARRAY_SIZE(usp0_muxmask),
+	.muxmask = usp0_muxmask,
+	.funcmask = BIT(1) | BIT(2) | BIT(6) | BIT(9),
+	.funcval = 0,
+};
+
+static const unsigned usp0_pins[] = { 51, 52, 53, 54, 55 };
+
+static const struct sirfsoc_muxmask usp1_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(24) | BIT(25) | BIT(26) | BIT(27) | BIT(28),
+	},
+};
+
+static const struct sirfsoc_padmux usp1_padmux = {
+	.muxmask_counts = ARRAY_SIZE(usp1_muxmask),
+	.muxmask = usp1_muxmask,
+	.funcmask = BIT(1) | BIT(9) | BIT(10) | BIT(11),
+	.funcval = 0,
+};
+
+static const unsigned usp1_pins[] = { 56, 57, 58, 59, 60 };
+
+static const struct sirfsoc_muxmask usp2_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(29) | BIT(30) | BIT(31),
+	}, {
+		.group = 2,
+		.mask = BIT(0) | BIT(1),
+	},
+};
+
+static const struct sirfsoc_padmux usp2_padmux = {
+	.muxmask_counts = ARRAY_SIZE(usp2_muxmask),
+	.muxmask = usp2_muxmask,
+	.funcmask = BIT(13) | BIT(14),
+	.funcval = 0,
+};
+
+static const unsigned usp2_pins[] = { 61, 62, 63, 64, 65 };
+
+static const struct sirfsoc_muxmask nand_muxmask[] = {
+	{
+		.group = 2,
+		.mask = BIT(2) | BIT(3) | BIT(28) | BIT(29) | BIT(30),
+	},
+};
+
+static const struct sirfsoc_padmux nand_padmux = {
+	.muxmask_counts = ARRAY_SIZE(nand_muxmask),
+	.muxmask = nand_muxmask,
+	.funcmask = BIT(5),
+	.funcval = 0,
+};
+
+static const unsigned nand_pins[] = { 64, 65, 92, 93, 94 };
+
+static const struct sirfsoc_padmux sdmmc0_padmux = {
+	.muxmask_counts = 0,
+	.funcmask = BIT(5),
+	.funcval = 0,
+};
+
+static const unsigned sdmmc0_pins[] = { };
+
+static const struct sirfsoc_muxmask sdmmc2_muxmask[] = {
+	{
+		.group = 2,
+		.mask = BIT(2) | BIT(3),
+	},
+};
+
+static const struct sirfsoc_padmux sdmmc2_padmux = {
+	.muxmask_counts = ARRAY_SIZE(sdmmc2_muxmask),
+	.muxmask = sdmmc2_muxmask,
+	.funcmask = BIT(5),
+	.funcval = BIT(5),
+};
+
+static const unsigned sdmmc2_pins[] = { 66, 67 };
+
+static const struct sirfsoc_muxmask cko0_muxmask[] = {
+	{
+		.group = 2,
+		.mask = BIT(14),
+	},
+};
+
+static const struct sirfsoc_padmux cko0_padmux = {
+	.muxmask_counts = ARRAY_SIZE(cko0_muxmask),
+	.muxmask = cko0_muxmask,
+};
+
+static const unsigned cko0_pins[] = { 78 };
+
+static const struct sirfsoc_muxmask vip_muxmask[] = {
+	{
+		.group = 2,
+		.mask = BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19)
+			| BIT(20) | BIT(21) | BIT(22) | BIT(23) | BIT(24) |
+			BIT(25),
+	},
+};
+
+static const struct sirfsoc_padmux vip_padmux = {
+	.muxmask_counts = ARRAY_SIZE(vip_muxmask),
+	.muxmask = vip_muxmask,
+	.funcmask = BIT(0),
+	.funcval = 0,
+};
+
+static const unsigned vip_pins[] = { 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89 };
+
+static const struct sirfsoc_muxmask i2c0_muxmask[] = {
+	{
+		.group = 2,
+		.mask = BIT(26) | BIT(27),
+	},
+};
+
+static const struct sirfsoc_padmux i2c0_padmux = {
+	.muxmask_counts = ARRAY_SIZE(i2c0_muxmask),
+	.muxmask = i2c0_muxmask,
+};
+
+static const unsigned i2c0_pins[] = { 90, 91 };
+
+static const struct sirfsoc_muxmask i2c1_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(13) | BIT(15),
+	},
+};
+
+static const struct sirfsoc_padmux i2c1_padmux = {
+	.muxmask_counts = ARRAY_SIZE(i2c1_muxmask),
+	.muxmask = i2c1_muxmask,
+};
+
+static const unsigned i2c1_pins[] = { 13, 15 };
+
+static const struct sirfsoc_muxmask viprom_muxmask[] = {
+	{
+		.group = 2,
+		.mask = BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19)
+			| BIT(20) | BIT(21) | BIT(22) | BIT(23) | BIT(24) |
+			BIT(25),
+	}, {
+		.group = 0,
+		.mask = BIT(12),
+	},
+};
+
+static const struct sirfsoc_padmux viprom_padmux = {
+	.muxmask_counts = ARRAY_SIZE(viprom_muxmask),
+	.muxmask = viprom_muxmask,
+	.funcmask = BIT(0),
+	.funcval = BIT(0),
+};
+
+static const unsigned viprom_pins[] = { 12, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89 };
+
+static const struct sirfsoc_muxmask pwm0_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(4),
+	},
+};
+
+static const struct sirfsoc_padmux pwm0_padmux = {
+	.muxmask_counts = ARRAY_SIZE(pwm0_muxmask),
+	.muxmask = pwm0_muxmask,
+	.funcmask = BIT(12),
+	.funcval = 0,
+};
+
+static const unsigned pwm0_pins[] = { 4 };
+
+static const struct sirfsoc_muxmask pwm1_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(5),
+	},
+};
+
+static const struct sirfsoc_padmux pwm1_padmux = {
+	.muxmask_counts = ARRAY_SIZE(pwm1_muxmask),
+	.muxmask = pwm1_muxmask,
+};
+
+static const unsigned pwm1_pins[] = { 5 };
+
+static const struct sirfsoc_muxmask pwm2_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(6),
+	},
+};
+
+static const struct sirfsoc_padmux pwm2_padmux = {
+	.muxmask_counts = ARRAY_SIZE(pwm2_muxmask),
+	.muxmask = pwm2_muxmask,
+};
+
+static const unsigned pwm2_pins[] = { 6 };
+
+static const struct sirfsoc_muxmask pwm3_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(7),
+	},
+};
+
+static const struct sirfsoc_padmux pwm3_padmux = {
+	.muxmask_counts = ARRAY_SIZE(pwm3_muxmask),
+	.muxmask = pwm3_muxmask,
+};
+
+static const unsigned pwm3_pins[] = { 7 };
+
+static const struct sirfsoc_muxmask warm_rst_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(8),
+	},
+};
+
+static const struct sirfsoc_padmux warm_rst_padmux = {
+	.muxmask_counts = ARRAY_SIZE(warm_rst_muxmask),
+	.muxmask = warm_rst_muxmask,
+};
+
+static const unsigned warm_rst_pins[] = { 8 };
+
+static const struct sirfsoc_muxmask usb0_utmi_drvbus_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(22),
+	},
+};
+static const struct sirfsoc_padmux usb0_utmi_drvbus_padmux = {
+	.muxmask_counts = ARRAY_SIZE(usb0_utmi_drvbus_muxmask),
+	.muxmask = usb0_utmi_drvbus_muxmask,
+	.funcmask = BIT(6),
+	.funcval = BIT(6), /* refer to PAD_UTMI_DRVVBUS0_ENABLE */
+};
+
+static const unsigned usb0_utmi_drvbus_pins[] = { 54 };
+
+static const struct sirfsoc_muxmask usb1_utmi_drvbus_muxmask[] = {
+	{
+		.group = 1,
+		.mask = BIT(27),
+	},
+};
+
+static const struct sirfsoc_padmux usb1_utmi_drvbus_padmux = {
+	.muxmask_counts = ARRAY_SIZE(usb1_utmi_drvbus_muxmask),
+	.muxmask = usb1_utmi_drvbus_muxmask,
+	.funcmask = BIT(11),
+	.funcval = BIT(11), /* refer to PAD_UTMI_DRVVBUS1_ENABLE */
+};
+
+static const unsigned usb1_utmi_drvbus_pins[] = { 59 };
+
+static const struct sirfsoc_muxmask pulse_count_muxmask[] = {
+	{
+		.group = 0,
+		.mask = BIT(9) | BIT(10) | BIT(11),
+	},
+};
+
+static const struct sirfsoc_padmux pulse_count_padmux = {
+	.muxmask_counts = ARRAY_SIZE(pulse_count_muxmask),
+	.muxmask = pulse_count_muxmask,
+};
+
+static const unsigned pulse_count_pins[] = { 9, 10, 11 };
+
+#define SIRFSOC_PIN_GROUP(n, p)  \
+	{			\
+		.name = n,	\
+		.pins = p,	\
+		.num_pins = ARRAY_SIZE(p),	\
+	}
+
+static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = {
+	SIRFSOC_PIN_GROUP("lcd_16bitsgrp", lcd_16bits_pins),
+	SIRFSOC_PIN_GROUP("lcd_18bitsgrp", lcd_18bits_pins),
+	SIRFSOC_PIN_GROUP("lcd_24bitsgrp", lcd_24bits_pins),
+	SIRFSOC_PIN_GROUP("lcdrom_grp", lcdrom_pins),
+	SIRFSOC_PIN_GROUP("uart0grp", uart0_pins),
+	SIRFSOC_PIN_GROUP("uart1grp", uart1_pins),
+	SIRFSOC_PIN_GROUP("uart2grp", uart2_pins),
+	SIRFSOC_PIN_GROUP("uart2_nostreamctrlgrp", uart2_nostreamctrl_pins),
+	SIRFSOC_PIN_GROUP("usp0grp", usp0_pins),
+	SIRFSOC_PIN_GROUP("usp1grp", usp1_pins),
+	SIRFSOC_PIN_GROUP("usp2grp", usp2_pins),
+	SIRFSOC_PIN_GROUP("i2c0grp", i2c0_pins),
+	SIRFSOC_PIN_GROUP("i2c1grp", i2c1_pins),
+	SIRFSOC_PIN_GROUP("pwm0grp", pwm0_pins),
+	SIRFSOC_PIN_GROUP("pwm1grp", pwm1_pins),
+	SIRFSOC_PIN_GROUP("pwm2grp", pwm2_pins),
+	SIRFSOC_PIN_GROUP("pwm3grp", pwm3_pins),
+	SIRFSOC_PIN_GROUP("vipgrp", vip_pins),
+	SIRFSOC_PIN_GROUP("vipromgrp", viprom_pins),
+	SIRFSOC_PIN_GROUP("warm_rstgrp", warm_rst_pins),
+	SIRFSOC_PIN_GROUP("cko0_rstgrp", cko0_pins),
+	SIRFSOC_PIN_GROUP("cko1_rstgrp", cko1_pins),
+	SIRFSOC_PIN_GROUP("sdmmc0grp", sdmmc0_pins),
+	SIRFSOC_PIN_GROUP("sdmmc1grp", sdmmc1_pins),
+	SIRFSOC_PIN_GROUP("sdmmc2grp", sdmmc2_pins),
+	SIRFSOC_PIN_GROUP("sdmmc3grp", sdmmc3_pins),
+	SIRFSOC_PIN_GROUP("sdmmc4grp", sdmmc4_pins),
+	SIRFSOC_PIN_GROUP("sdmmc5grp", sdmmc5_pins),
+	SIRFSOC_PIN_GROUP("usb0_utmi_drvbusgrp", usb0_utmi_drvbus_pins),
+	SIRFSOC_PIN_GROUP("usb1_utmi_drvbusgrp", usb1_utmi_drvbus_pins),
+	SIRFSOC_PIN_GROUP("pulse_countgrp", pulse_count_pins),
+	SIRFSOC_PIN_GROUP("i2sgrp", i2s_pins),
+	SIRFSOC_PIN_GROUP("ac97grp", ac97_pins),
+	SIRFSOC_PIN_GROUP("nandgrp", nand_pins),
+	SIRFSOC_PIN_GROUP("spi0grp", spi0_pins),
+	SIRFSOC_PIN_GROUP("spi1grp", spi1_pins),
+	SIRFSOC_PIN_GROUP("gpsgrp", gps_pins),
+};
+
+static int sirfsoc_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+{
+	if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
+		return -EINVAL;
+	return 0;
+}
+
+static const char *sirfsoc_get_group_name(struct pinctrl_dev *pctldev,
+				       unsigned selector)
+{
+	if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
+		return NULL;
+	return sirfsoc_pin_groups[selector].name;
+}
+
+static int sirfsoc_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
+			       unsigned ** const pins,
+			       unsigned * const num_pins)
+{
+	if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
+		return -EINVAL;
+	*pins = (unsigned *) sirfsoc_pin_groups[selector].pins;
+	*num_pins = sirfsoc_pin_groups[selector].num_pins;
+	return 0;
+}
+
+static void sirfsoc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
+		   unsigned offset)
+{
+	seq_printf(s, " " DRIVER_NAME);
+}
+
+static struct pinctrl_ops sirfsoc_pctrl_ops = {
+	.list_groups = sirfsoc_list_groups,
+	.get_group_name = sirfsoc_get_group_name,
+	.get_group_pins = sirfsoc_get_group_pins,
+	.pin_dbg_show = sirfsoc_pin_dbg_show,
+};
+
+struct sirfsoc_pmx_func {
+	const char *name;
+	const char * const *groups;
+	const unsigned num_groups;
+	const struct sirfsoc_padmux *padmux;
+};
+
+static const char * const lcd_16bitsgrp[] = { "lcd_16bitsgrp" };
+static const char * const lcd_18bitsgrp[] = { "lcd_18bitsgrp" };
+static const char * const lcd_24bitsgrp[] = { "lcd_24bitsgrp" };
+static const char * const lcdromgrp[] = { "lcdromgrp" };
+static const char * const uart0grp[] = { "uart0grp" };
+static const char * const uart1grp[] = { "uart1grp" };
+static const char * const uart2grp[] = { "uart2grp" };
+static const char * const uart2_nostreamctrlgrp[] = { "uart2_nostreamctrlgrp" };
+static const char * const usp0grp[] = { "usp0grp" };
+static const char * const usp1grp[] = { "usp1grp" };
+static const char * const usp2grp[] = { "usp2grp" };
+static const char * const i2c0grp[] = { "i2c0grp" };
+static const char * const i2c1grp[] = { "i2c1grp" };
+static const char * const pwm0grp[] = { "pwm0grp" };
+static const char * const pwm1grp[] = { "pwm1grp" };
+static const char * const pwm2grp[] = { "pwm2grp" };
+static const char * const pwm3grp[] = { "pwm3grp" };
+static const char * const vipgrp[] = { "vipgrp" };
+static const char * const vipromgrp[] = { "vipromgrp" };
+static const char * const warm_rstgrp[] = { "warm_rstgrp" };
+static const char * const cko0grp[] = { "cko0grp" };
+static const char * const cko1grp[] = { "cko1grp" };
+static const char * const sdmmc0grp[] = { "sdmmc0grp" };
+static const char * const sdmmc1grp[] = { "sdmmc1grp" };
+static const char * const sdmmc2grp[] = { "sdmmc2grp" };
+static const char * const sdmmc3grp[] = { "sdmmc3grp" };
+static const char * const sdmmc4grp[] = { "sdmmc4grp" };
+static const char * const sdmmc5grp[] = { "sdmmc5grp" };
+static const char * const usb0_utmi_drvbusgrp[] = { "usb0_utmi_drvbusgrp" };
+static const char * const usb1_utmi_drvbusgrp[] = { "usb1_utmi_drvbusgrp" };
+static const char * const pulse_countgrp[] = { "pulse_countgrp" };
+static const char * const i2sgrp[] = { "i2sgrp" };
+static const char * const ac97grp[] = { "ac97grp" };
+static const char * const nandgrp[] = { "nandgrp" };
+static const char * const spi0grp[] = { "spi0grp" };
+static const char * const spi1grp[] = { "spi1grp" };
+static const char * const gpsgrp[] = { "gpsgrp" };
+
+#define SIRFSOC_PMX_FUNCTION(n, g, m)		\
+	{					\
+		.name = n,			\
+		.groups = g,			\
+		.num_groups = ARRAY_SIZE(g),	\
+		.padmux = &m,			\
+	}
+
+static const struct sirfsoc_pmx_func sirfsoc_pmx_functions[] = {
+	SIRFSOC_PMX_FUNCTION("lcd_16bits", lcd_16bitsgrp, lcd_16bits_padmux),
+	SIRFSOC_PMX_FUNCTION("lcd_18bits", lcd_18bitsgrp, lcd_18bits_padmux),
+	SIRFSOC_PMX_FUNCTION("lcd_24bits", lcd_24bitsgrp, lcd_24bits_padmux),
+	SIRFSOC_PMX_FUNCTION("lcdrom", lcdromgrp, lcdrom_padmux),
+	SIRFSOC_PMX_FUNCTION("uart0", uart0grp, uart0_padmux),
+	SIRFSOC_PMX_FUNCTION("uart1", uart1grp, uart1_padmux),
+	SIRFSOC_PMX_FUNCTION("uart2", uart2grp, uart2_padmux),
+	SIRFSOC_PMX_FUNCTION("uart2_nostreamctrl", uart2_nostreamctrlgrp, uart2_nostreamctrl_padmux),
+	SIRFSOC_PMX_FUNCTION("usp0", usp0grp, usp0_padmux),
+	SIRFSOC_PMX_FUNCTION("usp1", usp1grp, usp1_padmux),
+	SIRFSOC_PMX_FUNCTION("usp2", usp2grp, usp2_padmux),
+	SIRFSOC_PMX_FUNCTION("i2c0", i2c0grp, i2c0_padmux),
+	SIRFSOC_PMX_FUNCTION("i2c1", i2c1grp, i2c1_padmux),
+	SIRFSOC_PMX_FUNCTION("pwm0", pwm0grp, pwm0_padmux),
+	SIRFSOC_PMX_FUNCTION("pwm1", pwm1grp, pwm1_padmux),
+	SIRFSOC_PMX_FUNCTION("pwm2", pwm2grp, pwm2_padmux),
+	SIRFSOC_PMX_FUNCTION("pwm3", pwm3grp, pwm3_padmux),
+	SIRFSOC_PMX_FUNCTION("vip", vipgrp, vip_padmux),
+	SIRFSOC_PMX_FUNCTION("viprom", vipromgrp, viprom_padmux),
+	SIRFSOC_PMX_FUNCTION("warm_rst", warm_rstgrp, warm_rst_padmux),
+	SIRFSOC_PMX_FUNCTION("cko0", cko0grp, cko0_padmux),
+	SIRFSOC_PMX_FUNCTION("cko1", cko1grp, cko1_padmux),
+	SIRFSOC_PMX_FUNCTION("sdmmc0", sdmmc0grp, sdmmc0_padmux),
+	SIRFSOC_PMX_FUNCTION("sdmmc1", sdmmc1grp, sdmmc1_padmux),
+	SIRFSOC_PMX_FUNCTION("sdmmc2", sdmmc2grp, sdmmc2_padmux),
+	SIRFSOC_PMX_FUNCTION("sdmmc3", sdmmc3grp, sdmmc3_padmux),
+	SIRFSOC_PMX_FUNCTION("sdmmc4", sdmmc4grp, sdmmc4_padmux),
+	SIRFSOC_PMX_FUNCTION("sdmmc5", sdmmc5grp, sdmmc5_padmux),
+	SIRFSOC_PMX_FUNCTION("usb0_utmi_drvbus", usb0_utmi_drvbusgrp, usb0_utmi_drvbus_padmux),
+	SIRFSOC_PMX_FUNCTION("usb1_utmi_drvbus", usb1_utmi_drvbusgrp, usb1_utmi_drvbus_padmux),
+	SIRFSOC_PMX_FUNCTION("pulse_count", pulse_countgrp, pulse_count_padmux),
+	SIRFSOC_PMX_FUNCTION("i2s", i2sgrp, i2s_padmux),
+	SIRFSOC_PMX_FUNCTION("ac97", ac97grp, ac97_padmux),
+	SIRFSOC_PMX_FUNCTION("nand", nandgrp, nand_padmux),
+	SIRFSOC_PMX_FUNCTION("spi0", spi0grp, spi0_padmux),
+	SIRFSOC_PMX_FUNCTION("spi1", spi1grp, spi1_padmux),
+	SIRFSOC_PMX_FUNCTION("gps", gpsgrp, gps_padmux),
+};
+
+static void sirfsoc_pinmux_endisable(struct sirfsoc_pmx *spmx, unsigned selector,
+	bool enable)
+{
+	int i;
+	const struct sirfsoc_padmux *mux = sirfsoc_pmx_functions[selector].padmux;
+	const struct sirfsoc_muxmask *mask = mux->muxmask;
+
+	for (i = 0; i < mux->muxmask_counts; i++) {
+		u32 muxval;
+		muxval = readl(spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(mask[i].group));
+		if (enable)
+			muxval = muxval & ~mask[i].mask;
+		else
+			muxval = muxval | mask[i].mask;
+		writel(muxval, spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(mask[i].group));
+	}
+
+	if (mux->funcmask && enable) {
+		u32 func_en_val;
+		func_en_val =
+			readl(spmx->rsc_virtbase + SIRFSOC_RSC_PIN_MUX);
+		func_en_val =
+			(func_en_val & ~mux->funcmask) | (mux->
+				funcval);
+		writel(func_en_val, spmx->rsc_virtbase + SIRFSOC_RSC_PIN_MUX);
+	}
+}
+
+static int sirfsoc_pinmux_enable(struct pinctrl_dev *pmxdev, unsigned selector,
+	unsigned group)
+{
+	struct sirfsoc_pmx *spmx;
+
+	spmx = pctldev_get_drvdata(pmxdev);
+	sirfsoc_pinmux_endisable(spmx, selector, true);
+
+	return 0;
+}
+
+static void sirfsoc_pinmux_disable(struct pinctrl_dev *pmxdev, unsigned selector,
+	unsigned group)
+{
+	struct sirfsoc_pmx *spmx;
+
+	spmx = pctldev_get_drvdata(pmxdev);
+	sirfsoc_pinmux_endisable(spmx, selector, false);
+}
+
+static int sirfsoc_pinmux_list_funcs(struct pinctrl_dev *pmxdev, unsigned selector)
+{
+	if (selector >= ARRAY_SIZE(sirfsoc_pmx_functions))
+		return -EINVAL;
+	return 0;
+}
+
+static const char *sirfsoc_pinmux_get_func_name(struct pinctrl_dev *pctldev,
+					  unsigned selector)
+{
+	return sirfsoc_pmx_functions[selector].name;
+}
+
+static int sirfsoc_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
+			       const char * const **groups,
+			       unsigned * const num_groups)
+{
+	*groups = sirfsoc_pmx_functions[selector].groups;
+	*num_groups = sirfsoc_pmx_functions[selector].num_groups;
+	return 0;
+}
+
+static int sirfsoc_pinmux_request_gpio(struct pinctrl_dev *pmxdev,
+	struct pinctrl_gpio_range *range, unsigned offset)
+{
+	struct sirfsoc_pmx *spmx;
+
+	int group = range->id;
+
+	u32 muxval;
+
+	spmx = pctldev_get_drvdata(pmxdev);
+
+	muxval = readl(spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(group));
+	muxval = muxval | (1 << offset);
+	writel(muxval, spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(group));
+
+	return 0;
+}
+
+static struct pinmux_ops sirfsoc_pinmux_ops = {
+	.list_functions = sirfsoc_pinmux_list_funcs,
+	.enable = sirfsoc_pinmux_enable,
+	.disable = sirfsoc_pinmux_disable,
+	.get_function_name = sirfsoc_pinmux_get_func_name,
+	.get_function_groups = sirfsoc_pinmux_get_groups,
+	.gpio_request_enable = sirfsoc_pinmux_request_gpio,
+};
+
+static struct pinctrl_desc sirfsoc_pinmux_desc = {
+	.name = DRIVER_NAME,
+	.pins = sirfsoc_pads,
+	.npins = ARRAY_SIZE(sirfsoc_pads),
+	.maxpin = SIRFSOC_NUM_PADS - 1,
+	.pctlops = &sirfsoc_pctrl_ops,
+	.pmxops = &sirfsoc_pinmux_ops,
+	.owner = THIS_MODULE,
+};
+
+/*
+ * Todo: bind irq_chip to every pinctrl_gpio_range
+ */
+static struct pinctrl_gpio_range sirfsoc_gpio_ranges[] = {
+	{
+		.name = "sirfsoc-gpio*",
+		.id = 0,
+		.base = 0,
+		.npins = 32,
+	}, {
+		.name = "sirfsoc-gpio*",
+		.id = 1,
+		.base = 32,
+		.npins = 32,
+	}, {
+		.name = "sirfsoc-gpio*",
+		.id = 2,
+		.base = 64,
+		.npins = 32,
+	}, {
+		.name = "sirfsoc-gpio*",
+		.id = 3,
+		.base = 96,
+		.npins = 19,
+	},
+};
+
+static void __iomem *sirfsoc_rsc_of_iomap(void)
+{
+	const struct of_device_id rsc_ids[]  = {
+		{ .compatible = "sirf,prima2-rsc" },
+		{}
+	};
+	struct device_node *np;
+
+	np = of_find_matching_node(NULL, rsc_ids);
+	if (!np)
+		panic("unable to find compatible rsc node in dtb\n");
+
+	return of_iomap(np, 0);
+}
+
+static int __devinit sirfsoc_pinmux_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct sirfsoc_pmx *spmx;
+	struct device_node *np = pdev->dev.of_node;
+	int i;
+
+	/* Create state holders etc for this driver */
+	spmx = devm_kzalloc(&pdev->dev, sizeof(*spmx), GFP_KERNEL);
+	if (!spmx)
+		return -ENOMEM;
+
+	spmx->dev = &pdev->dev;
+
+	platform_set_drvdata(pdev, spmx);
+
+	spmx->gpio_virtbase = of_iomap(np, 0);
+	if (!spmx->gpio_virtbase) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "can't map gpio registers\n");
+		goto out_no_gpio_remap;
+	}
+
+	spmx->rsc_virtbase = sirfsoc_rsc_of_iomap();
+	if (!spmx->rsc_virtbase) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "can't map rsc registers\n");
+		goto out_no_rsc_remap;
+	}
+
+	/* Now register the pin controller and all pins it handles */
+	spmx->pmx = pinctrl_register(&sirfsoc_pinmux_desc, &pdev->dev, spmx);
+	if (IS_ERR(spmx->pmx)) {
+		dev_err(&pdev->dev, "could not register SIRFSOC pinmux driver\n");
+		ret = PTR_ERR(spmx->pmx);
+		goto out_no_pmx;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(sirfsoc_gpio_ranges); i++)
+		pinctrl_add_gpio_range(spmx->pmx, &sirfsoc_gpio_ranges[i]);
+
+	dev_info(&pdev->dev, "initialized SIRFSOC pinmux driver\n");
+
+	return 0;
+
+out_no_pmx:
+	iounmap(spmx->rsc_virtbase);
+out_no_rsc_remap:
+	iounmap(spmx->gpio_virtbase);
+out_no_gpio_remap:
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, spmx);
+	return ret;
+}
+
+static const struct of_device_id pinmux_ids[]  = {
+	{ .compatible = "sirf,prima2-gpio-pinmux" },
+	{}
+};
+
+static struct platform_driver sirfsoc_pinmux_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = pinmux_ids,
+	},
+	.probe = sirfsoc_pinmux_probe,
+};
+
+static int __init sirfsoc_pinmux_init(void)
+{
+	return platform_driver_register(&sirfsoc_pinmux_driver);
+}
+arch_initcall(sirfsoc_pinmux_init);
+
+MODULE_AUTHOR("Rongjun Ying <rongjun.ying@csr.com>, "
+	"Barry Song <baohua.song@csr.com>");
+MODULE_DESCRIPTION("SIRFSOC pin control driver");
+MODULE_LICENSE("GPL");
-- 
1.7.1



Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom
More information can be found at www.csr.com. Follow CSR on Twitter at http://twitter.com/CSR_PLC and read our blog at www.csr.com/blog

^ permalink raw reply related

* [PATCH 17/26] ARM: pxa: pxa95x is incompatible with earlier pxa
From: Arnd Bergmann @ 2011-10-09 11:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAMPhdO8_cZU8FEGsK0m_XrhbR33TiZFFaNrXddKT1YrHREt-TQ@mail.gmail.com>

On Sunday 09 October 2011 14:36:52 Eric Miao wrote:
> 
> They are actually not mutually exclusive - it's a trick we use to select
> MACH_TAVOREVB3 by default but this option also disappears once SAARB
> is selected.

Right. It's a bit hard to read when there are only two of them, but that
is the idea.

> The 'depends on ARCH_PXA_V7' could actually be removed here, as the
> option is already encapsulated in if ARCH_PXA_V7 ... endif. And this
> trick won't work very well when there are more than 2 boards.

Actually, the option is outside of the if ARCH_PXA_V7, but that could
easily be changed.

The trick works with as many boards as you like, it just gets a
little clumsy after while, as in

config MACH_OMAP_AUTO_BOARD
        def_bool y
        depends on !MACH_OMAP2_TUSB6010
        depends on !MACH_OMAP_H4
        depends on !MACH_OMAP_APOLLON
        depends on !MACH_OMAP_APOLLON
        depends on !MACH_OMAP_2430SDP
        depends on !MACH_OMAP3_BEAGLE
        depends on !MACH_DEVKIT8000
        depends on !MACH_OMAP_LDP
        depends on !MACH_OMAP3530_LV_SOM
        depends on !MACH_OMAP3_TORPEDO
        depends on !MACH_OVERO
        depends on !MACH_OMAP3EVM
        depends on !MACH_OMAP3517EVM
        depends on !MACH_CRANEBOARD
        depends on !MACH_OMAP3_PANDORA
        depends on !MACH_OMAP3_TOUCHBOOK
        depends on !MACH_NOKIA_N8X0
        depends on !MACH_NOKIA_RM680
        depends on !MACH_NOKIA_RX51
        depends on !MACH_OMAP_ZOOM2
        depends on !MACH_OMAP_ZOOM3
        depends on !MACH_CM_T35
        depends on !MACH_CM_T3517
        depends on !MACH_IGEP0020
        depends on !MACH_IGEP0030
        depends on !MACH_SBC3530
        depends on !MACH_OMAP_3630SDP
        depends on !MACH_TI8168EVM
        depends on !MACH_OMAP4_PANDA
        select MACH_OMAP_GENERIC if ARCH_OMAP2
        select MACH_OMAP_3430SDP if ARCH_OMAP3 && !ARCH_OMAP2
        select MACH_OMAP_4430SDP if ARCH_OMAP4 && !ARCH_OMAP3 && !ARCH_OMAP2
        help
          The kernel needs to have support for at least one board
          in order to build. If none is selected, default to the
          generic board.

We should probably do something like this for the ARMv5 based PXA boards,
but at least a randconfig won't trigger that normally, just because there
are so many of them that almost always at least one is enabled. However,
an allnoconfig will fail with PXA right now.

	Arnd

^ permalink raw reply

* [PATCH 17/26] ARM: pxa: pxa95x is incompatible with earlier pxa
From: Arnd Bergmann @ 2011-10-09 11:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAN1soZyDu43EN3W5YBttdzx7qx=MLKAR1OsJC3hUdqWWxhQaew@mail.gmail.com>

On Sunday 09 October 2011 14:21:35 Haojian Zhuang wrote:
> > If we move it to arch/arm/Kconfig, I would prefer making it a global option,
> > not a pxa specific one. If we introduce a top-level CONFIG_CPU_V6PLUS
> > option, we can make a number of decisions inside of Kconfig depend on that,
> > especially as we move to allow building multiple v6/v7 platforms together,
> > or multiple v5 platforms for that matter. I believe we don't need to
> > worry about v5+v7 at this point and can instead assume that won't ever
> > happen.
> >
> Nobody is using PJ4 as v6 architecture now. CPUv6 is used in old stepping
> of Dove and MMP2. CPU_PJ4 only enables CPU_v7 in the mainline code.
> 
> If we used ARCH_PXA_V7 in arch/arm/Kconfig, we would have two ARCH for
> pxa. One is ARCH_PXA, and the other is ARCH_PXA_V7. Those v5 machine
> should be based on ARCH_PXA. And the saarb, tavorevb3 should be based
> on ARCH_PXA_V7. So we can avoid to define PXA_V7_MACH_AUTO. I think
> the logic of Kconfig could be easier.

I was referring to building different subarchitectures together.
We want to eventually be able to build a single kernel that
works on e.g. pxa95x (ARMv7) and omap2 (ARMv6), and we hope to get
there within the next few release. This is completely unrelated to
building a combination of old-world (ARMv3/v4/v5) and new-world
(ARMv6/v7) in a single kernel, which is something that we decided
not to try supporting in the forseeable future.

	Arnd

^ permalink raw reply

* [GIT PULL] I2C: OMAP: misc. cleanup for v3.2
From: Ming Lei @ 2011-10-09 11:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <87aa9f771n.fsf@ti.com>

On Thu, Oct 6, 2011 at 12:11 AM, Kevin Hilman <khilman@ti.com> wrote:
> Ben Dooks <ben-i2c@fluff.org> writes:
>
>> On Mon, Sep 26, 2011 at 03:30:50PM -0700, Kevin Hilman wrote:
>>> ping
>>>
>>> On 09/06/2011 03:31 PM, Kevin Hilman wrote:
>>> >Hi Ben,
>>> >
>>> >On 08/23/2011 05:10 PM, Kevin Hilman wrote:
>>> >>Ben,
>>> >>
>>> >>Here's one more I2C cleanup series for v3.2.
>>> >>
>>> >>It applies on top of my for_3.2/i2c-fixes branch just submitted.
>>> >>
>>> >>Please pull into your tree for linux-next.
>>> >
>>> >I see you pulled the other two, can you pull this one as well?
>>
>> I've tried, but it seems to note that everything is up to date.
>>
>> Is this a suitable branch to pull onto latest so I can reset
>> my next tree?
>
> Yes.
>
> The i2c-cleanup branch[1] is based on top of the previous two (i2c-andy
> and i2c-fixes) so if you reset your next-i2c branch and just pull
> i2c-cleanup, you'll get all three.

BTW:

The patch[1] is a fix patch, which fix the OOPS[2] during system resume, so
should CC -stable tree.

[1], I2C: OMAP: remove dev->idle, use usage counting provided by runtime PM
[2], http://marc.info/?l=linux-omap&m=131658936429671&w=2

thanks,
-- 
Ming Lei

^ permalink raw reply

* orion/kirkwood and device tree support
From: Michael Walle @ 2011-10-09 13:45 UTC (permalink / raw)
  To: linux-arm-kernel


Dear orion/kirkwood maintainers,

I submitted a patch to support the Buffalo LS-XHL three months ago, which was 
rejected because no more old fashioned board setup patches will be merged. 

Are there any plans or even ongoing developments to convert these platforms to 
support device trees?

What is the best way to convert the {orion,kirkwood}_*_init() calls to DT? 
Make each device use a device_driver structure for initialization?

-- 
Michael

^ permalink raw reply

* Add support for the SPMP8000 SoC and Letcool board
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This series adds support for the Sunplus SPMP8000 SoC, an ARM926EJ-S based
MCU, which is most commonly found in handheld game consoles and digital cams.
Most of the work was done during GSoC 2011, with Greg KH as my mentor.

The board I'm using for development is the Letcool N350JP handheld.

The patches are based on Arnds for-next branch with Jamie Iles' vic-dt patches
on top.

No static platform devices are used, everything is inited from DT, with some
hacks for drivers that don't have any bindings yet.
(Documentation for own, custom bindings will follow in next version.)

So far, these peripherals are supported, with drivers not submitted yet:
gpio, fb, cpufreq, slave-dma, mmc, ASoC (i2s, codec, pcm, card).

Reviews, instructions for mainlining it are very welcome.

Thanks, Cheers,
Zoltan

 arch/arm/Kconfig                                  |   16 +
 arch/arm/Makefile                                 |    1 +
 arch/arm/boot/dts/spmp8000-letcool.dts            |  163 ++++++
 arch/arm/boot/dts/spmp8000.dtsi                   |  169 ++++++
 arch/arm/common/vic.c                             |    1 -
 arch/arm/mach-spmp8000/Makefile                   |   11 +
 arch/arm/mach-spmp8000/Makefile.boot              |    1 +
 arch/arm/mach-spmp8000/adc.c                      |  465 ++++++++++++++++
 arch/arm/mach-spmp8000/board_letcool.c            |  154 ++++++
 arch/arm/mach-spmp8000/clkdev.c                   |  586 +++++++++++++++++++++
 arch/arm/mach-spmp8000/clock.c                    |  155 ++++++
 arch/arm/mach-spmp8000/core.c                     |  103 ++++
 arch/arm/mach-spmp8000/include/mach/clock.h       |   37 ++
 arch/arm/mach-spmp8000/include/mach/core.h        |   29 +
 arch/arm/mach-spmp8000/include/mach/debug-macro.S |   19 +
 arch/arm/mach-spmp8000/include/mach/dma.h         |   45 ++
 arch/arm/mach-spmp8000/include/mach/entry-macro.S |   14 +
 arch/arm/mach-spmp8000/include/mach/gpio.h        |   21 +
 arch/arm/mach-spmp8000/include/mach/hardware.h    |   27 +
 arch/arm/mach-spmp8000/include/mach/io.h          |   20 +
 arch/arm/mach-spmp8000/include/mach/irqs.h        |   21 +
 arch/arm/mach-spmp8000/include/mach/memory.h      |   16 +
 arch/arm/mach-spmp8000/include/mach/regs-timer.h  |   32 ++
 arch/arm/mach-spmp8000/include/mach/scu.h         |  146 +++++
 arch/arm/mach-spmp8000/include/mach/spmp8000adc.h |   29 +
 arch/arm/mach-spmp8000/include/mach/spmp8000fb.h  |   32 ++
 arch/arm/mach-spmp8000/include/mach/system.h      |   45 ++
 arch/arm/mach-spmp8000/include/mach/timex.h       |   17 +
 arch/arm/mach-spmp8000/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-spmp8000/include/mach/vmalloc.h     |   16 +
 arch/arm/mach-spmp8000/pinmux.c                   |  131 +++++
 arch/arm/mach-spmp8000/pwm.c                      |  246 +++++++++
 arch/arm/mach-spmp8000/timer.c                    |  160 ++++++
 33 files changed, 2964 insertions(+), 1 deletions(-)

^ permalink raw reply

* [PATCH 1/9] ARM: vic: Don't write to the read-only register VIC_IRQ_STATUS
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

This is unneeded and causes an abort on the SPMP8000 platform.

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/common/vic.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c
index b22b83d..651c77d 100644
--- a/arch/arm/common/vic.c
+++ b/arch/arm/common/vic.c
@@ -274,7 +274,6 @@ static void __init vic_disable(void __iomem *base)
 	writel(0, base + VIC_INT_SELECT);
 	writel(0, base + VIC_INT_ENABLE);
 	writel(~0, base + VIC_INT_ENABLE_CLEAR);
-	writel(0, base + VIC_IRQ_STATUS);
 	writel(0, base + VIC_ITCR);
 	writel(~0, base + VIC_INT_SOFT_CLEAR);
 }
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 2/9] ARM: SPMP8000: Add machine base files
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

Add the files for the basic support of the SPMP8000 SoC

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/mach-spmp8000/Makefile                   |   11 ++
 arch/arm/mach-spmp8000/Makefile.boot              |    1 +
 arch/arm/mach-spmp8000/core.c                     |  103 +++++++++++++
 arch/arm/mach-spmp8000/include/mach/core.h        |   29 ++++
 arch/arm/mach-spmp8000/include/mach/debug-macro.S |   19 +++
 arch/arm/mach-spmp8000/include/mach/dma.h         |   45 ++++++
 arch/arm/mach-spmp8000/include/mach/entry-macro.S |   14 ++
 arch/arm/mach-spmp8000/include/mach/gpio.h        |   21 +++
 arch/arm/mach-spmp8000/include/mach/hardware.h    |   27 ++++
 arch/arm/mach-spmp8000/include/mach/io.h          |   20 +++
 arch/arm/mach-spmp8000/include/mach/irqs.h        |   21 +++
 arch/arm/mach-spmp8000/include/mach/memory.h      |   16 ++
 arch/arm/mach-spmp8000/include/mach/regs-timer.h  |   32 ++++
 arch/arm/mach-spmp8000/include/mach/scu.h         |  146 +++++++++++++++++++
 arch/arm/mach-spmp8000/include/mach/system.h      |   45 ++++++
 arch/arm/mach-spmp8000/include/mach/timex.h       |   17 +++
 arch/arm/mach-spmp8000/include/mach/uncompress.h  |   37 +++++
 arch/arm/mach-spmp8000/include/mach/vmalloc.h     |   16 ++
 arch/arm/mach-spmp8000/timer.c                    |  160 +++++++++++++++++++++
 19 files changed, 780 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/Makefile
 create mode 100644 arch/arm/mach-spmp8000/Makefile.boot
 create mode 100644 arch/arm/mach-spmp8000/core.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/core.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-spmp8000/include/mach/dma.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-spmp8000/include/mach/gpio.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/hardware.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/io.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/irqs.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/memory.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/regs-timer.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/scu.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/system.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/timex.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-spmp8000/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-spmp8000/timer.c

diff --git a/arch/arm/mach-spmp8000/Makefile b/arch/arm/mach-spmp8000/Makefile
new file mode 100644
index 0000000..9d35cca
--- /dev/null
+++ b/arch/arm/mach-spmp8000/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the linux kernel.
+#
+obj-y 			:= core.o
+obj-y			+= timer.o
+obj-y			+= pinmux.o
+obj-y			+= clock.o
+obj-y			+= clkdev.o
+obj-y			+= board_letcool.o
+obj-y			+= adc.o
+obj-y			+= pwm.o
diff --git a/arch/arm/mach-spmp8000/Makefile.boot b/arch/arm/mach-spmp8000/Makefile.boot
new file mode 100644
index 0000000..dae9661
--- /dev/null
+++ b/arch/arm/mach-spmp8000/Makefile.boot
@@ -0,0 +1 @@
+zreladdr-y	:= 0x00008000
diff --git a/arch/arm/mach-spmp8000/core.c b/arch/arm/mach-spmp8000/core.c
new file mode 100644
index 0000000..ba05614
--- /dev/null
+++ b/arch/arm/mach-spmp8000/core.c
@@ -0,0 +1,103 @@
+/*
+ * SPMP8000 machines core functions
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/vic.h>
+#include <mach/hardware.h>
+#include <mach/scu.h>
+
+/* Static mappings:
+ * SCU: timer needs clk support which is inited in init_early
+ * UART: needed for earlyprintk
+ */
+static struct map_desc io_desc[] __initdata = {
+	{
+		.virtual	= IO_ADDRESS(SPMP8000_SCU_A_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_SCU_A_BASE),
+		.length		= SPMP8000_SCU_A_SIZE,
+		.type		= MT_DEVICE,
+	}, {
+		.virtual	= IO_ADDRESS(SPMP8000_SCU_B_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_SCU_B_BASE),
+		.length		= SPMP8000_SCU_B_SIZE,
+		.type		= MT_DEVICE,
+	}, {
+		.virtual	= IO_ADDRESS(SPMP8000_SCU_C_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_SCU_C_BASE),
+		.length		= SPMP8000_SCU_C_SIZE,
+		.type		= MT_DEVICE,
+	},
+#ifdef CONFIG_DEBUG_LL
+	{
+		.virtual	= IO_ADDRESS(SPMP8000_UARTC0_BASE),
+		.pfn		= __phys_to_pfn(SPMP8000_UARTC0_BASE),
+		.length		= SPMP8000_UARTC0_SIZE,
+		.type		= MT_DEVICE,
+	},
+#endif
+};
+
+#define IO_PTR(x)	((void __iomem *)IO_ADDRESS(x))
+/* Make the lookup of SCU base and clk enable registers easy for the clk
+ * code, as they are not at the same offset in each controller */
+struct scu_reg_t scu_regs[3] = {
+	{	/* SCU_A */
+		.base = IO_PTR(SPMP8000_SCU_A_BASE),
+		.clken = IO_PTR(SPMP8000_SCU_A_BASE) + SCU_A_PERI_CLKEN,
+	},
+	{	/* SCU_B */
+		.base = IO_PTR(SPMP8000_SCU_B_BASE),
+		.clken = IO_PTR(SPMP8000_SCU_B_BASE) + SCU_B_PERI_CLKEN,
+	},
+	{	/* SCU_C */
+		.base = IO_PTR(SPMP8000_SCU_C_BASE),
+		.clken = IO_PTR(SPMP8000_SCU_C_BASE) + SCU_C_PERI_CLKEN,
+	},
+};
+
+/* DMA controller names to be used for the filter
+ * function of the DMA-Engine API. */
+char *spmp8000_dma_controller_names[] = {
+	"93010000.dma",		/* APBDMA_A */
+	"92b00000.dma",		/* APBDMA_C */
+};
+
+void __init spmp8000_map_io(void)
+{
+	iotable_init(io_desc, ARRAY_SIZE(io_desc));
+}
+
+void __init spmp8000_init_irq(void)
+{
+	struct device_node *np;
+	int ret;
+
+	np = of_find_compatible_node(NULL, NULL, "arm,pl192");
+	if (!np)
+		panic("Can't find VIC0 interrupt controller\n");
+
+	ret = vic_of_init(np, NULL);
+	if (ret)
+		panic("Can't init VIC0 interrupt controller\n");
+
+	np = of_find_compatible_node(np, NULL, "arm,pl192");
+	if (!np)
+		panic("Can't find VIC1 interrupt controller\n");
+
+	ret = vic_of_init(np, NULL);
+	if (ret)
+		panic("Can't init VIC1 interrupt controller\n");
+
+	of_node_put(np);
+}
diff --git a/arch/arm/mach-spmp8000/include/mach/core.h b/arch/arm/mach-spmp8000/include/mach/core.h
new file mode 100644
index 0000000..fa1d522
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/core.h
@@ -0,0 +1,29 @@
+/*
+ * SPMP8000 generic includes
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_GENERIC_H__
+#define __MACH_SPMP8000_GENERIC_H__
+
+/* core.c */
+extern void __init spmp8000_map_io(void);
+extern void __init spmp8000_init_irq(void);
+extern struct sys_timer spmp8000_sys_timer;
+
+/* clkdev.c */
+extern void __init spmp8000_init_clkdev(void);
+extern void spmp8000_update_arm_freqs(void); /* For cpufreq driver */
+
+/* pinmux.c */
+extern void spmp8000_pinmux_pgc_set(int pc, unsigned int conf);
+extern unsigned int spmp8000_pinmux_pgc_get(int pc);
+extern void spmp8000_pinmux_pgs_set(unsigned int pg, unsigned int func);
+extern int spmp8000_pinmux_pgs_get(int pg);
+
+#endif /* __MACH_SPMP8000_GENERIC_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/debug-macro.S b/arch/arm/mach-spmp8000/include/mach/debug-macro.S
new file mode 100644
index 0000000..698c37e
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/debug-macro.S
@@ -0,0 +1,19 @@
+/*
+ * SPMP8000 debug-macro.S
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+	.macro  addruart, rp, rv
+	mov     \rp, #0x92000000
+	add	\rp, #0x00B00000
+	add	\rp, #0x00004000
+	orr	\rv, \rp, #0xF0000000
+	.endm
+
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
diff --git a/arch/arm/mach-spmp8000/include/mach/dma.h b/arch/arm/mach-spmp8000/include/mach/dma.h
new file mode 100644
index 0000000..44bc9f2
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/dma.h
@@ -0,0 +1,45 @@
+/*
+ * SPMP8000 dma.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/module.h>
+#include <linux/dmaengine.h>
+
+#ifndef __MACH_SPMP8000_DMA_H__
+#define __MACH_SPMP8000_DMA_H__
+
+enum spmp8000_dma_controller {
+	SPMP8000_APBDMA_A	= 0,
+	SPMP8000_APBDMA_C,
+};
+
+extern char *spmp8000_dma_controller_names[];
+
+struct spmp8000_dma_params {
+	char				*dma_controller;
+	dma_addr_t			dma_address;
+	enum dma_slave_buswidth		dma_width;
+	int				maxburst;
+};
+
+/* Driver parameters */
+#define SPMP8000_APBDMA_MAX_PERIODS	64
+
+static bool spmp8000_dma_filter(struct dma_chan *chan, const char *name)
+{
+	int ret;
+
+	ret = strcmp(dev_name(chan->device->dev), name);
+
+	if (ret)
+		return false;
+
+	return true;
+}
+
+#endif /* __MACH_SPMP8000_DMA_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/entry-macro.S b/arch/arm/mach-spmp8000/include/mach/entry-macro.S
new file mode 100644
index 0000000..3d26088
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/entry-macro.S
@@ -0,0 +1,14 @@
+/*
+ * SPMP8000 entry-macro.S
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+	.macro  disable_fiq
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
diff --git a/arch/arm/mach-spmp8000/include/mach/gpio.h b/arch/arm/mach-spmp8000/include/mach/gpio.h
new file mode 100644
index 0000000..3eafac2
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/gpio.h
@@ -0,0 +1,21 @@
+/*
+ * SPMP8000 machines gpio support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_GPIO_H__
+#define __MACH_SPMP8000_GPIO_H__
+
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value		__gpio_get_value
+#define gpio_set_value		__gpio_set_value
+#define gpio_cansleep		__gpio_cansleep
+#define gpio_to_irq		__gpio_to_irq
+
+#endif /* __MACH_SPMP8000_GPIO_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/hardware.h b/arch/arm/mach-spmp8000/include/mach/hardware.h
new file mode 100644
index 0000000..998a184
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/hardware.h
@@ -0,0 +1,27 @@
+/*
+ * SPMP8000 hardware.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_HARDWARE_H__
+#define __MACH_SPMP8000_HARDWARE_H__
+
+/* Vitual to physical translation of statically mapped space */
+#define IO_ADDRESS(x)		((x) | 0xF0000000)
+
+/* Needed for the static mappings and uncompress.h */
+#define SPMP8000_UARTC0_BASE	0x92B04000
+#define SPMP8000_UARTC0_SIZE	SZ_4K
+#define SPMP8000_SCU_C_BASE	0x92005000
+#define SPMP8000_SCU_C_SIZE	SZ_4K
+#define SPMP8000_SCU_A_BASE	0x93007000
+#define SPMP8000_SCU_A_SIZE	SZ_4K
+#define SPMP8000_SCU_B_BASE	0x90005000
+#define SPMP8000_SCU_B_SIZE	SZ_4K
+
+#endif /* __MACH_SPMP8000_HARDWARE_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/io.h b/arch/arm/mach-spmp8000/include/mach/io.h
new file mode 100644
index 0000000..a8ea8f0
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/io.h
@@ -0,0 +1,20 @@
+/*
+ * SPMP8000 io.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_IO_H__
+#define __MACH_SPMP8000_IO_H__
+
+/* FIXME This should go away */
+#define IO_SPACE_LIMIT  0
+
+#define __mem_pci(a)    (a)
+#define __io(a)         __typesafe_io(a)
+
+#endif /* __MACH_SPMP8000_IO_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/irqs.h b/arch/arm/mach-spmp8000/include/mach/irqs.h
new file mode 100644
index 0000000..7f305d3
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/irqs.h
@@ -0,0 +1,21 @@
+/*
+ * SPMP8000 irqs.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_IRQS_H__
+#define __MACH_SPMP8000_IRQS_H__
+
+#define SPMP8000_HW_IRQS		64
+#define SPMP8000_GPIO_IRQS_START	SPMP8000_HW_IRQS
+#define SPMP8000_GPIO_IRQS		(16 + 32)
+#define SPMP8000_TOTAL_IRQS		(SPMP8000_HW_IRQS + SPMP8000_GPIO_IRQS)
+
+#define NR_IRQS				SPMP8000_TOTAL_IRQS
+
+#endif /* __MACH_SPMP8000_IRQS_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/memory.h b/arch/arm/mach-spmp8000/include/mach/memory.h
new file mode 100644
index 0000000..d2f1089
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/memory.h
@@ -0,0 +1,16 @@
+/*
+ * SPMP8000 memory.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_MEMORY_H__
+#define __MACH_SPMP8000_MEMORY_H__
+
+#define PLAT_PHYS_OFFSET	UL(0x00000000)
+
+#endif /* __MACH_SPMP8000_MEMORY_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/regs-timer.h b/arch/arm/mach-spmp8000/include/mach/regs-timer.h
new file mode 100644
index 0000000..90735df
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/regs-timer.h
@@ -0,0 +1,32 @@
+/*
+ * SPMP8000 timer support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * These timer reg definitions are used by the timer and pwm drivers
+ */
+#ifndef __MACH_SPMP8000_REGS_TIMER_H__
+#define __MACH_SPMP8000_REGS_TIMER_H__
+
+#define SPMP8000_TMRB(tnum)	(tnum * 0x20)
+#define SPMP8000_TMRB_CTR	0x00
+#define SPMP8000_TMRB_CTR_TE	BIT(0)
+#define SPMP8000_TMRB_CTR_IE	BIT(1)
+#define SPMP8000_TMRB_CTR_OE	BIT(2)
+#define SPMP8000_TMRB_CTR_PWMON	BIT(3)
+#define SPMP8000_TMRB_CTR_UD	BIT(4)
+#define SPMP8000_TMRB_CTR_UDS	BIT(5)
+#define SPMP8000_TMRB_CTR_OM	BIT(6)
+#define SPMP8000_TMRB_CTR_ES_SH	8
+#define SPMP8000_TMRB_CTR_M_SH	10
+#define SPMP8000_TMRB_PSR	0x04
+#define SPMP8000_TMRB_LDR	0x08
+#define SPMP8000_TMRB_VLR	0x08
+#define SPMP8000_TMRB_ISR	0x0C
+#define SPMP8000_TMRB_CMP	0x10
+
+#endif /* __MACH_SPMP8000_REGS_TIMER_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/scu.h b/arch/arm/mach-spmp8000/include/mach/scu.h
new file mode 100644
index 0000000..e3a98d4
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/scu.h
@@ -0,0 +1,146 @@
+/*
+ * SPMP8000 System Control Unit register definitions
+ * SCUs are a random collection of reset, clock, gpio, pinmux, etc. controllers
+ * so that these definitions are needed at several places.
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bitops.h>
+#include <mach/hardware.h>
+
+#ifndef __MACH_SPMP8000_SCU_H__
+#define __MACH_SPMP8000_SCU_H__
+
+/* Used by to the clock code to directly access the SCU base and clock
+ * enabling registers, without looking up the according registers first.
+ */
+struct scu_reg_t {
+	void *base;
+	void *clken;
+};
+
+extern struct scu_reg_t scu_regs[];
+
+#define REG_SCU_BASE(x)		scu_regs[x].base
+#define REG_SCU_CLKEN(x)	scu_regs[x].clken
+#define REG_SCU_A_ID		0
+#define REG_SCU_B_ID		1
+#define REG_SCU_C_ID		2
+#define REG_SCU_A(x)		(scu_regs[REG_SCU_A_ID].base + x)
+#define REG_SCU_B(x)		(scu_regs[REG_SCU_B_ID].base + x)
+#define REG_SCU_C(x)		(scu_regs[REG_SCU_C_ID].base + x)
+
+#define SCU_A_PERI_RST		0x00
+#define SCU_A_PERI_CLKEN	0x04
+#define SCU_A_PERI_DGCLKEN	0x0C
+
+#define SCU_A_APLL_CFG		0x44
+#define APLL_CFG_P		BIT(0)
+#define APLL_CFG_S		BIT(1)
+#define APLL_CFG_F		BIT(2)
+#define APLL_CFG_E		BIT(3)
+#define APLL_CFG_AS_MASK	0x3F0
+#define APLL_CFG_AS_SHIFT	4
+#define APLL_CFG_AS_MAGIC	0x12
+#define APLL_CFG_C		BIT(10)
+#define APLL_CFG_R		BIT(11)
+#define APLL_CFG_DAR_SHIFT	16
+#define APLL_CFG_DAR_MASK	(7 << APLL_CFG_DAR_SHIFT)
+#define APLL_CFG_DS		BIT(19)
+#define APLL_CFG_ADR_SHIFT	24
+#define APLL_CFG_ADR_MASK	(7 << APLL_CFG_ADR_SHIFT)
+#define APLL_CFG_AS		BIT(27)
+
+#define SCU_A_LCD_CLK_CFG	0x80
+#define LCD_CLK_CFG_RATIO_MASK	0xFF
+#define LCD_CLK_CFG_EN		BIT(8)
+#define SCU_A_CSI_CLK_CFG	0x84
+#define CSI_CLK_CFG_RATIO_MASK	0xFF
+#define CSI_CLK_CFG_EN		BIT(8)
+#define SCU_A_I2S_BCK_CFG	0x90
+#define I2S_BCK_CFG_RATIO_MASK	0xFF
+#define I2S_BCK_CFG_EN		BIT(8)
+#define SCU_A_UART_CFG		0x94
+#define UART_CFG_RATIO_MASK	0xFF
+#define UART_CFG_EN		BIT(8)
+#define DIVIDER_ENABLE_BIT	BIT(8)
+
+#define SCU_A_CODEC_CFG		0xB0
+#define SCU_A_CODEC_CFG_VAL	0x5A
+
+#define SCU_A_SAR_GPIO_CTRL	0xE0
+#define SCU_A_SAR_GPIO_OEN	0xE4
+#define SCU_A_SAR_GPIO_O	0xE8
+#define SCU_A_SAR_GPIO_I	0xEC
+
+#define SCU_B_PERI_RST		0x00
+#define SCU_B_PERI_CLKEN	0x20
+#define SCU_B_PERI_DGCLKEN	0x24
+#define SCU_B_UPDATE_ARM_RATIO	0x28
+
+#define SCU_B_SPLL_CFG		0x04
+#define SPLL_CFG_R_MASK		3
+#define SPLL_CFG_R_SHIFT	0
+#define SPLL_CFG_F_MASK		0xFC
+#define SPLL_CFG_F_SHIFT	2
+#define SPLL_CFG_BS		BIT(8)
+#define SPLL_CFG_OD		BIT(9)
+#define SPLL_CFG_BP		BIT(10)
+#define SPLL_CFG_PD		BIT(11)
+#define SPLL_CFG_CSEL_MASK	(BIT(13) | BIT(12))
+#define SPLL_CFG_CSEL_SHIFT	12
+#define SPLL_CFG_ASEL_MASK	(BIT(15) | BIT(14))
+#define SPLL_CFG_ASEL_SHIFT	14
+#define SPLL_CFG_SE		BIT(17)
+#define SPLL_CFG_XE		BIT(18)
+#define SPLL_CFG_AE		BIT(19)
+#define SPLL_CFG_XR		BIT(20)
+#define SPLL_CFG_PL		BIT(31)
+
+#define SCU_B_GPIO3_I		0x70
+#define SCU_B_GPIO3_O		0x74
+#define SCU_B_GPIO3_E		0x78
+
+#define SCU_B_PGS0		0x80
+#define SCU_B_PGS1		0x84
+#define SCU_B_PGS2		0x88
+#define SCU_B_PGS3		0x8C
+#define SCU_B_PGC0		0x90
+#define SCU_B_PGC1		0x94
+#define SCU_B_PGC2		0x98
+#define SCU_B_PGC3		0x9C
+
+#define SCU_B_ARM_RATIO		0xD0
+#define SCU_B_ARM_AHB_RATIO	0xD4
+#define SCU_B_ARM_APB_RATIO	0xD8
+#define SCU_B_SYS_CNT_EN	0xDC
+#define SYS_CNT_EN_SYS		BIT(0)
+#define SYS_CNT_EN_SYS_RT	BIT(1)
+#define SYS_CNT_EN_SYS_AHB	BIT(2)
+#define SYS_CNT_EN_SYS_APB	BIT(3)
+#define SYS_CNT_EN_SYS_CHECK	BIT(31)
+
+#define SCU_C_PERI_RST		0x00
+#define SCU_C_PERI_CLKEN	0x04
+#define SCU_C_PERI_DGCLKEN	0x08
+
+#define SCU_C_UPDATE_SYS_RATIO	0x28
+#define SCU_C_SYS_RATIO		0x100
+#define SCU_C_SYS_RT_RATIO	0x104
+#define SCU_C_SYS_AHB_RATIO	0x108
+#define SCU_C_SYS_APB_RATIO	0x10C
+#define SCU_C_CEVA_RATIO	0x110
+#define SCU_C_CEVA_AHB_RATIO	0x114
+#define SCU_C_CEVA_APB_RATIO	0x118
+#define SCU_C_CEVA_CNT_EN	0x11C
+#define CEVA_CNT_EN_CEVA	BIT(0)
+#define CEVA_CNT_EN_CEVA_AHB	BIT(1)
+#define CEVA_CNT_EN_CEVA_APB	BIT(2)
+#define CEVA_CNT_EN_CEVA_CHECK	BIT(31)
+
+#endif /* __MACH_SPMP8000_SCU_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/system.h b/arch/arm/mach-spmp8000/include/mach/system.h
new file mode 100644
index 0000000..be53ff3
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/system.h
@@ -0,0 +1,45 @@
+/*
+ * SPMP8000 system.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_SYSTEM_H__
+#define __MACH_SPMP8000_SYSTEM_H__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#define SPMP8000_WDT_BASE	0x90001000
+#define SPMP8000_WDT_SIZE	0x1000
+
+#define SPMP8000_WDT_CTR	0x00
+#define SPMP8000_WDT_CTR_TE	BIT(0)
+#define SPMP8000_WDT_CTR_RE	BIT(3)
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	void *base;
+
+	base = ioremap(SPMP8000_WDT_BASE, SPMP8000_WDT_SIZE);
+	if (!base) {
+		pr_err("spmp8000: Can't ioremap watchdog regs for reset. "
+			"Halt.");
+		while (1);
+	}
+
+	/* Trigger a watchdog reset */
+	writel(SPMP8000_WDT_CTR_RE | SPMP8000_WDT_CTR_TE,
+			base + SPMP8000_WDT_CTR);
+}
+
+#endif /* __MACH_SPMP8000_SYSTEM_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/timex.h b/arch/arm/mach-spmp8000/include/mach/timex.h
new file mode 100644
index 0000000..77069c2
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/timex.h
@@ -0,0 +1,17 @@
+/*
+ * SPMP8000 timex.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_TIMEX_H__
+#define __MACH_SPMP8000_TIMEX_H__
+
+/* TODO this should go away */
+#define CLOCK_TICK_RATE		(100 * HZ)
+
+#endif /* __MACH_SPMP8000_TIMEX_H__ */
diff --git a/arch/arm/mach-spmp8000/include/mach/uncompress.h b/arch/arm/mach-spmp8000/include/mach/uncompress.h
new file mode 100644
index 0000000..105778b
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/*
+ * SPMP8000 uncompress.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * Based on the mach-kirkwood implementation
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/serial_reg.h>
+#include <mach/hardware.h>
+
+#define SERIAL_BASE	((volatile unsigned char *)SPMP8000_UARTC0_BASE)
+
+static void putc(const char c)
+{
+	volatile unsigned char *base = SERIAL_BASE;
+	int i;
+
+	for (i = 0; i < 0x1000; i++) {
+		if (base[UART_LSR << 2] & UART_LSR_THRE)
+			break;
+		barrier();
+	}
+
+	base[UART_TX << 2] = c;
+}
+
+static void flush(void)
+{
+}
+
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-spmp8000/include/mach/vmalloc.h b/arch/arm/mach-spmp8000/include/mach/vmalloc.h
new file mode 100644
index 0000000..ff40d1c
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/vmalloc.h
@@ -0,0 +1,16 @@
+/*
+ * SPMP8000 vmalloc.h
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_VMALLOC_H__
+#define __MACH_SPMP8000_VMALLOC_H__
+
+#define VMALLOC_END	0xF0000000UL
+
+#endif /* __MACH_SPMP8000_VMALLOC_H__ */
diff --git a/arch/arm/mach-spmp8000/timer.c b/arch/arm/mach-spmp8000/timer.c
new file mode 100644
index 0000000..7d5d0eb
--- /dev/null
+++ b/arch/arm/mach-spmp8000/timer.c
@@ -0,0 +1,160 @@
+/*
+ * SPMP8000 machines timer functions
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/clk.h>
+#include <asm/mach/time.h>
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-timer.h>
+#include <mach/irqs.h>
+
+static unsigned long clkrate;
+static const unsigned int tickrate = 1012500;
+
+/* The TIMER_B block is used as system timer
+ * T2: Clocksource
+ * T1: Clockevent
+ * T0: PWM for LCD backlight
+ * T3,4: Could be used as additional clockevent devices
+ * Timer constraints:
+ * - WDT: Can't clear the irq directly, only by resetting the whole counter
+ *   in the ISR, which means that IRQs will jitter
+ * - Timer_B: Can't reset the timer value, so at start, the first IRQ
+ *   will happen at some random time.
+ */
+#define		CS_TIMER	2
+#define		CE_TIMER	1
+static void __iomem *cs_base;
+static void __iomem *ce_base;
+
+static void tmrb_set_mode(enum clock_event_mode mode,
+				struct clock_event_device *dev)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		writel((tickrate / HZ), ce_base + SPMP8000_TMRB_LDR);
+		/* Configure as periodic, down counter, IEN, enable timer */
+		writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_IE |
+				(1 << SPMP8000_TMRB_CTR_M_SH),
+				ce_base + SPMP8000_TMRB_CTR);
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+		BUG();
+		break;
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	case CLOCK_EVT_MODE_UNUSED:
+		/* Disable timer */
+		writel(0, ce_base + SPMP8000_TMRB_CTR);
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+		BUG();
+		break;
+	}
+}
+
+static struct clock_event_device tmrb1_clkevt = {
+	.name		= "tmrb1",
+	.features	= CLOCK_EVT_FEAT_PERIODIC,
+	.rating		= 200,
+	.set_mode	= tmrb_set_mode,
+};
+
+static irqreturn_t tmrb1_isr(int irq, void *dev_id)
+{
+	tmrb1_clkevt.event_handler(&tmrb1_clkevt);
+
+	/* Clear IRQ */
+	writel(0, ce_base + SPMP8000_TMRB_ISR);
+
+	return IRQ_HANDLED;
+};
+
+static struct irqaction tmrb1_irq = {
+	.name		= "tmrb1_irq",
+	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
+	.handler	= tmrb1_isr,
+};
+
+static void __init spmp8000_sys_timer_init(void)
+{
+	struct device_node *np;
+	unsigned int irq;
+	struct clk *clk;
+	void *tmrb_base;
+
+	np = of_find_compatible_node(NULL, NULL, "sunplus,spmp8000-timer");
+	if (!np)
+		panic("spmp8000: unable to find timer node in dtb\n");
+
+	tmrb_base = of_iomap(np, 0);
+	if (!tmrb_base)
+		panic("spmp8000: unable to map timer cpu registers\n");
+
+	irq = of_irq_to_resource(np, CE_TIMER, NULL);
+	if (irq == NO_IRQ)
+		panic("spmp8000: unable to get interrupts of timer\n");
+
+	of_node_put(np);
+
+	cs_base = tmrb_base + SPMP8000_TMRB(CS_TIMER);
+	ce_base = tmrb_base + SPMP8000_TMRB(CE_TIMER);
+
+	clk = clk_get(NULL, "arm_apb");
+	if (IS_ERR_OR_NULL(clk))
+		panic("spmp8000: Can't get clock for timer device");
+
+	clk_enable(clk);
+	clkrate = clk_get_rate(clk);
+
+	/* Clocksource */
+	/* Disable timer */
+	writel(0, cs_base + SPMP8000_TMRB_CTR);
+
+	/* Reset counter value
+	 * Not really possible unless setting end-1 LDR value and waiting
+	 * until the counter reaches that */
+
+	/* Prescale timer */
+	writel((clkrate / tickrate) - 1, cs_base + SPMP8000_TMRB_PSR);
+
+	/* Register the clocksource */
+	clocksource_mmio_init(cs_base + SPMP8000_TMRB_VLR, "tmrb2",
+				tickrate, 200, 16, clocksource_mmio_readl_up);
+
+	/* Configure as free running (0 - 0xFFFF), up counter, enable timer */
+	writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_UD,
+		cs_base + SPMP8000_TMRB_CTR);
+
+	/* Clockevent */
+	setup_irq(irq, &tmrb1_irq);
+
+	/* Disable timer */
+	writel(0, ce_base + SPMP8000_TMRB_CTR);
+
+	/* Prescale timer */
+	writel((clkrate / tickrate) - 1, ce_base + SPMP8000_TMRB_PSR);
+
+	clockevents_register_device(&tmrb1_clkevt);
+}
+
+struct sys_timer spmp8000_sys_timer = {
+	.init		= spmp8000_sys_timer_init,
+};
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 3/9] ARM: SPMP8000: Add clk support
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/mach-spmp8000/clkdev.c             |  586 +++++++++++++++++++++++++++
 arch/arm/mach-spmp8000/clock.c              |  155 +++++++
 arch/arm/mach-spmp8000/include/mach/clock.h |   37 ++
 3 files changed, 778 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/clkdev.c
 create mode 100644 arch/arm/mach-spmp8000/clock.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/clock.h

diff --git a/arch/arm/mach-spmp8000/clkdev.c b/arch/arm/mach-spmp8000/clkdev.c
new file mode 100644
index 0000000..c492d20
--- /dev/null
+++ b/arch/arm/mach-spmp8000/clkdev.c
@@ -0,0 +1,586 @@
+/*
+ * SPMP8000 machines clock support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <mach/clock.h>
+#include <mach/scu.h>
+
+/* The SoC doesn't support anything else, so no need to make these machine
+ * configurable.
+ */
+#define XTAL_FREQ		27000000UL
+#define XTAL_32K_FREQ		32768UL
+#define APLL_48K_FREQ		73728000UL
+#define APLL_44K_FREQ		67737600UL
+
+static unsigned long spll_get_rate(struct clk *clk)
+{
+	unsigned int f, r, od;
+	unsigned long osc;
+	u32 cfg;
+
+	cfg = readl(REG_SCU_B(SCU_B_SPLL_CFG));
+	osc = XTAL_FREQ;
+
+	/* SPLL off ? */
+	if ((cfg & (SPLL_CFG_XR | SPLL_CFG_PD | SPLL_CFG_BP)) ||
+		(~cfg & (SPLL_CFG_PL | SPLL_CFG_XE | SPLL_CFG_SE))) {
+		return 0;
+	}
+
+	f = (cfg & SPLL_CFG_F_MASK) >> SPLL_CFG_F_SHIFT;
+	r = (cfg & SPLL_CFG_R_MASK) >> SPLL_CFG_R_SHIFT;
+	od = !!(cfg & SPLL_CFG_OD);
+	clk->rate = (osc * (f + 1) * 2) / ((r + 1) * (od + 1));
+
+	return clk->rate;
+}
+
+static unsigned long pll_mux_get_rate(int ratesel, struct clk *clk)
+{
+	int pll_rate;
+
+	switch (ratesel) {
+	case 0:
+		clk->rate = XTAL_FREQ;
+		break;
+	case 1:
+		clk->rate = XTAL_32K_FREQ;
+		break;
+	case 2:
+		pll_rate = clk_get_rate(clk->parent);
+		clk->rate = pll_rate / 2;
+		break;
+	case 3:
+		pll_rate = clk_get_rate(clk->parent);
+		clk->rate = pll_rate / 3;
+		break;
+	}
+
+	return clk->rate;
+}
+
+static unsigned long ref_arm_get_rate(struct clk *clk)
+{
+	int ratesel;
+	u32 spll_cfg;
+
+	spll_cfg = readl(REG_SCU_B(SCU_B_SPLL_CFG));
+	ratesel = (spll_cfg & SPLL_CFG_ASEL_MASK) >> SPLL_CFG_ASEL_SHIFT;
+	return pll_mux_get_rate(ratesel, clk);
+}
+
+static unsigned long ref_ceva_get_rate(struct clk *clk)
+{
+	int ratesel;
+	u32 spll_cfg;
+
+	spll_cfg = readl(REG_SCU_B(SCU_B_SPLL_CFG));
+	ratesel = (spll_cfg & SPLL_CFG_CSEL_MASK) >> SPLL_CFG_CSEL_SHIFT;
+	return pll_mux_get_rate(ratesel, clk);
+}
+
+static unsigned long apll_get_rate(void)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	if ((cfg & APLL_CFG_R) || (~cfg & (APLL_CFG_P | APLL_CFG_E)) ||
+		(((cfg & APLL_CFG_AS_MASK) >> APLL_CFG_AS_SHIFT)
+			!= APLL_CFG_AS_MAGIC)) {
+		return 0;
+	}
+
+	/* Not dealing with the input oscillator frequency as the settings
+	 * for non-27Mhz are unknown, and all platforms use that anyway */
+	if (cfg & APLL_CFG_S)
+		return APLL_44K_FREQ;
+	else
+		return APLL_48K_FREQ;
+}
+
+static void apll_enable(struct clk *clk)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	/* Store new config with reset, then disable reset */
+	cfg |= APLL_CFG_R;
+	cfg |= APLL_CFG_E | APLL_CFG_P | APLL_CFG_F;
+	cfg |= APLL_CFG_AS_MAGIC << APLL_CFG_AS_SHIFT;
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+
+	cfg &= ~APLL_CFG_R;
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+}
+
+static void apll_disable(struct clk *clk)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	cfg &= ~APLL_CFG_P;
+	cfg |= APLL_CFG_E;
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+}
+
+static int apll_set_rate(unsigned long rate)
+{
+	u32 cfg;
+
+	cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	switch (rate) {
+	case 67737600:
+		cfg |= APLL_CFG_S;
+		break;
+	case 73728000:
+		cfg &= ~APLL_CFG_S;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+
+	return 0;
+}
+
+static void i2s_mck_switch(u32 mask, int enable)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+	if (enable)
+		cfg &= ~mask;
+	else
+		cfg |= mask;
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+}
+
+static void i2stx_mck_enable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_DS, 1);
+}
+
+static void i2stx_mck_disable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_DS, 0);
+}
+
+static void i2srx_mck_enable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_AS, 1);
+}
+
+static void i2srx_mck_disable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_AS, 0);
+}
+
+static const int apll_dividers[7] = {
+	3, 6, 9, 12, 18, 24, 32,
+};
+
+static unsigned long i2s_mck_get_rate(struct clk *clk, u32 msk, u32 sh)
+{
+	unsigned long apll_rate = apll_get_rate();
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+	int divider;
+
+	divider = (cfg & msk) >> sh;
+
+	if (divider == ARRAY_SIZE(apll_dividers))
+		return 0;
+
+	divider = apll_dividers[divider];
+
+	return apll_rate / divider;
+}
+
+static unsigned long i2stx_mck_get_rate(struct clk *clk)
+{
+	return i2s_mck_get_rate(clk, APLL_CFG_DAR_MASK, APLL_CFG_DAR_SHIFT);
+}
+
+static unsigned long i2srx_mck_get_rate(struct clk *clk)
+{
+	return i2s_mck_get_rate(clk, APLL_CFG_ADR_MASK, APLL_CFG_ADR_SHIFT);
+}
+
+static int i2s_mck_set_rate(struct clk *clk, unsigned long rate,
+					u32 msk, u32 sh)
+{
+	int i = ARRAY_SIZE(apll_dividers) - 1;
+	unsigned long apll_rate;
+	int divider = -1;
+	u32 cfg;
+
+	/* Set up APLL */
+	if (rate % 8000)
+		apll_rate = APLL_44K_FREQ;
+	else
+		apll_rate = APLL_48K_FREQ;
+
+	apll_set_rate(apll_rate);
+
+	cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	/* Get the biggest possible divider for MCK */
+	while (i >= 0) {
+		if (apll_rate / apll_dividers[i] == rate) {
+			divider = apll_dividers[i];
+			break;
+		}
+
+		i--;
+	}
+
+	if (divider < 0)
+		return -EINVAL;
+
+	cfg &= ~msk;
+	cfg |= (i << sh);
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+
+	return 0;
+}
+
+static int i2stx_mck_set_rate(struct clk *clk, unsigned long rate)
+{
+	return i2s_mck_set_rate(clk, rate,
+					APLL_CFG_DAR_MASK, APLL_CFG_DAR_SHIFT);
+}
+
+static int i2srx_mck_set_rate(struct clk *clk, unsigned long rate)
+{
+	return i2s_mck_set_rate(clk, rate,
+					APLL_CFG_ADR_MASK, APLL_CFG_ADR_SHIFT);
+}
+
+static int divider_set_clock(struct clk *clk, int on)
+{
+	u32 divider;
+
+	if (!(clk->flags & CLK_DIVIDER_WITH_ENABLE))
+		return -EINVAL;
+
+	divider = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	if (on)
+		divider |= DIVIDER_ENABLE_BIT;
+	else
+		divider = 0;
+	writel(divider, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	return 0;
+}
+
+static void divider_enable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 1);
+}
+
+static void divider_disable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 0);
+}
+
+static unsigned long divider_get_rate(struct clk *clk)
+{
+	u32 divider;
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+
+	if (!parent_rate) {
+		clk->rate = 0;
+		return clk->rate;
+	}
+
+	divider = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	if ((clk->flags & CLK_DIVIDER_WITH_ENABLE) &&
+		!(divider & DIVIDER_ENABLE_BIT)) {
+		clk->rate = 0UL;
+		return clk->rate;
+	}
+
+	clk->rate = parent_rate / ((divider & clk->divmask) + 1);
+
+	return clk->rate;
+}
+
+static int divider_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+	u32 divider, divider_old;
+
+	if (unlikely(!parent_rate || rate > parent_rate)) {
+		clk->rate = 0;
+		pr_debug("parent rate not sufficient\n");
+		return -EINVAL;
+	}
+
+	divider = (parent_rate / rate) - 1;
+
+	if (divider > clk->divmask) {
+		pr_debug("input clock too high\n");
+		return -EINVAL;
+	};
+
+	divider_old = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	writel(0, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	writel(divider, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	/* Re-enable clock if it was enabled before */
+	if (divider_old & DIVIDER_ENABLE_BIT)
+		writel(divider | DIVIDER_ENABLE_BIT,
+			REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	clk->rate = parent_rate / (divider + 1);
+
+	return 0;
+}
+static void periph_enable_clock(struct clk *clk)
+{
+	u32 scu_reg = readl(REG_SCU_CLKEN(clk->scu));
+
+	scu_reg |= (1 << clk->scu_periph_id);
+
+	writel(scu_reg, REG_SCU_CLKEN(clk->scu));
+}
+
+static void periph_disable_clock(struct clk *clk)
+{
+	u32 scu_reg = readl(REG_SCU_CLKEN(clk->scu));
+
+	scu_reg &= ~(1 << clk->scu_periph_id);
+
+	writel(scu_reg, REG_SCU_CLKEN(clk->scu));
+}
+
+void spmp8000_update_arm_freqs(void)
+{
+	writel(7, REG_SCU_B(SCU_B_UPDATE_ARM_RATIO));
+}
+EXPORT_SYMBOL(spmp8000_update_arm_freqs);
+
+static struct clk clk_spll = {
+	.get_rate = spll_get_rate,
+	.flags = CLK_FIXED_RATE,
+};
+
+static struct clk clk_ref_arm = {
+	.parent = &clk_spll,
+	.get_rate = ref_arm_get_rate,
+};
+
+static struct clk clk_ref_ceva = {
+	.parent = &clk_spll,
+	.get_rate = ref_ceva_get_rate,
+};
+
+static struct clk clk_apll = {
+	.name = "clk_apll",
+	.enable = apll_enable,
+	.disable = apll_disable,
+};
+
+static struct clk clk_i2stx_mck = {
+	.name = "clk_i2stx_mck",
+	.parent = &clk_apll,
+	.enable = i2stx_mck_enable,
+	.disable = i2stx_mck_disable,
+	.get_rate = i2stx_mck_get_rate,
+	.set_rate = i2stx_mck_set_rate,
+};
+
+static struct clk clk_i2srx_mck = {
+	.name = "clk_i2srx_mck",
+	.parent = &clk_apll,
+	.enable = i2srx_mck_enable,
+	.disable = i2srx_mck_disable,
+	.get_rate = i2srx_mck_get_rate,
+	.set_rate = i2srx_mck_set_rate,
+};
+
+#define SYSTEM_CLK(__name, __parent, __scu, __divider, __divmask, __flags) \
+static struct clk clk_##__name = { \
+	.name = "clk_" #__name, \
+	.parent = &clk_##__parent, \
+	.scu = REG_SCU_##__scu##_ID, \
+	.dividerreg = __divider, \
+	.divmask = __divmask, \
+	.flags = __flags, \
+	.enable = &divider_enable_clock, \
+	.disable = &divider_disable_clock, \
+	.get_rate = &divider_get_rate, \
+	.set_rate = &divider_set_rate, \
+}
+
+SYSTEM_CLK(arm, ref_arm, B, SCU_B_ARM_RATIO, 0x3F, 0);
+SYSTEM_CLK(arm_ahb, arm, B, SCU_B_ARM_AHB_RATIO, 0x3F, 0);
+SYSTEM_CLK(arm_apb, arm_ahb, B, SCU_B_ARM_APB_RATIO, 0x3F, 0);
+SYSTEM_CLK(ceva, ref_ceva, C, SCU_C_CEVA_RATIO, 0x3F, 0);
+SYSTEM_CLK(ceva_ahb, ceva, C, SCU_C_CEVA_AHB_RATIO, 0x3F, 0);
+SYSTEM_CLK(ceva_apb, ceva_ahb, C, SCU_C_CEVA_APB_RATIO, 0x3F, 0);
+SYSTEM_CLK(sys, ref_ceva, C, SCU_C_SYS_RATIO, 0x3F, 0);
+SYSTEM_CLK(sys_ahb, sys, C, SCU_C_SYS_AHB_RATIO, 0x3F, 0);
+SYSTEM_CLK(sys_apb, sys_ahb, C, SCU_C_SYS_APB_RATIO, 0x3F, 0);
+
+SYSTEM_CLK(uart, ref_ceva, A, SCU_A_UART_CFG, 0xFF, CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(lcd, ref_ceva, A, SCU_A_LCD_CLK_CFG, 0xFF, CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(csi, ref_ceva, A, SCU_A_CSI_CLK_CFG, 0xFF, CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(i2srx_bck, i2srx_mck, A, SCU_A_I2S_BCK_CFG, 0xFF,
+		CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(i2stx_bck, i2stx_mck, A, SCU_A_I2S_BCK_CFG, 0xFF,
+		CLK_DIVIDER_WITH_ENABLE);
+
+
+/* Peripherals */
+#define PERIPH_CLK(__name, __parent, __scu, __id) \
+static struct clk clk_##__name = { \
+	.name = "clk_" #__name, \
+	.parent = &__parent, \
+	.scu = REG_SCU_##__scu##_ID, \
+	.scu_periph_id = __id, \
+	.enable = periph_enable_clock, \
+	.disable = periph_disable_clock, \
+}
+
+/* Not needed yet for any driver */
+#if 0
+PERIPH_CLK(drm, clk_sys_ahb, A, 2);
+PERIPH_CLK(usb_host, clk_sys_ahb, A, 3);
+PERIPH_CLK(usb_device, clk_sys_ahb, A, 4);
+PERIPH_CLK(scu_a, clk_sys_ahb, A, 6);
+PERIPH_CLK(tvout, clk_sys, A, 7);
+PERIPH_CLK(csi_ctrl, clk_sys, A, 10);
+PERIPH_CLK(nand0, clk_sys_ahb, A, 11);
+PERIPH_CLK(nand1, clk_sys_ahb, A, 12);
+PERIPH_CLK(ecc, clk_sys_ahb, A, 13);
+PERIPH_CLK(uart_con, clk_sys_ahb, A, 15);
+PERIPH_CLK(aahbm212, clk_sys, A, 16);
+PERIPH_CLK(nand_abt, clk_sys_ahb, A, 20);
+PERIPH_CLK(rt_abt212, clk_sys, A, 22);
+PERIPH_CLK(cahbm212, clk_sys, A, 23);
+
+PERIPH_CLK(tcm_bist, clk_arm, B, 0);
+PERIPH_CLK(tcm_ctrl, clk_arm, B, 1);
+PERIPH_CLK(ahb2ahb, clk_arm, B, 2);
+PERIPH_CLK(ahbsw, clk_arm, B, 3);
+PERIPH_CLK(vic0, clk_arm_ahb, B, 4);
+PERIPH_CLK(vic1, clk_arm_ahb, B, 5);
+PERIPH_CLK(dpm, clk_arm_ahb, B, 6);
+PERIPH_CLK(apbb, clk_arm_ahb, B, 7);
+PERIPH_CLK(arm926, clk_arm, B, 8);
+PERIPH_CLK(wdt, clk_arm_apb, B, 10);
+PERIPH_CLK(uartapb, clk_arm_apb, B, 11);
+PERIPH_CLK(i2c, clk_arm_apb, B, 12);
+PERIPH_CLK(rand, clk_arm_apb, B, 13);
+PERIPH_CLK(gpio0, clk_arm_apb, B, 14);
+PERIPH_CLK(rtc, clk_arm_apb, B, 15);
+
+PERIPH_CLK(fabricc, clk_sys, C, 0);
+PERIPH_CLK(dmac0, clk_sys, C, 1);
+PERIPH_CLK(dmac1, clk_sys, C, 2);
+PERIPH_CLK(dram_ctrl, clk_sys, C, 4);
+PERIPH_CLK(scu_c, clk_sys, C, 5);
+PERIPH_CLK(i2c_ctrl, clk_sys, C, 6);
+PERIPH_CLK(2d, clk_sys, C, 8);
+PERIPH_CLK(extmem, clk_sys, C, 9);
+PERIPH_CLK(cf, clk_sys_apb, C, 10);
+PERIPH_CLK(ms, clk_sys_apb, C, 11);
+PERIPH_CLK(intmem, clk_sys, C, 12);
+PERIPH_CLK(uartc0, clk_sys_apb, C, 13);
+PERIPH_CLK(uartc1, clk_sys_apb, C, 14);
+PERIPH_CLK(uartc2, clk_sys_apb, C, 15);
+PERIPH_CLK(ssp0, clk_sys_apb, C, 16);
+PERIPH_CLK(ssp1, clk_sys_apb, C, 17);
+PERIPH_CLK(sd1, clk_sys_apb, C, 19);
+PERIPH_CLK(i2c_sys, clk_sys_apb, C, 20);
+PERIPH_CLK(scale, clk_sys, C, 21);
+PERIPH_CLK(scaleabt, clk_sys, C, 22);
+PERIPH_CLK(ti2c, clk_sys_apb, C, 23);
+PERIPH_CLK(fabric_a, clk_sys, C, 24);
+PERIPH_CLK(cxmp_sl, clk_sys, C, 25);
+PERIPH_CLK(cxmd_sl, clk_sys, C, 26);
+#endif
+
+PERIPH_CLK(rt_abt, clk_sys, A, 21);
+/* Make the parent rt_abt to auto-enable it when enabling the lcdc clock */
+PERIPH_CLK(lcd_ctrl, clk_rt_abt, A, 1);
+PERIPH_CLK(apbdma_a, clk_rt_abt, A, 9);
+PERIPH_CLK(apll_ctrl, clk_sys_ahb, A, 14);
+PERIPH_CLK(i2stx_ctrl, clk_apbdma_a, A, 17);
+PERIPH_CLK(i2srx_ctrl, clk_apbdma_a, A, 18);
+PERIPH_CLK(saacc, clk_sys_apb, A, 19);
+
+PERIPH_CLK(tmrb, clk_arm_apb, B, 9);
+PERIPH_CLK(apbdma_c, clk_sys_apb, C, 7);
+PERIPH_CLK(sd0, clk_sys_apb, C, 18);
+
+/* TODO use common macro once available */
+#define _DEFINE_CLKDEV_CON(n) \
+	{ \
+		.dev_id = NULL, \
+		.con_id = #n, \
+		.clk = &clk_##n, \
+	}
+
+#define _DEFINE_CLKDEV_DEV(d, c) \
+	{ \
+		.dev_id = d, \
+		.con_id = NULL, \
+		.clk = &clk_##c, \
+	}
+
+static struct clk_lookup lookups[] = {
+	_DEFINE_CLKDEV_CON(arm),
+	_DEFINE_CLKDEV_CON(arm_ahb),
+	_DEFINE_CLKDEV_CON(arm_apb),
+	_DEFINE_CLKDEV_CON(ceva),
+	_DEFINE_CLKDEV_CON(ceva_ahb),
+	_DEFINE_CLKDEV_CON(ceva_apb),
+	_DEFINE_CLKDEV_CON(sys),
+	_DEFINE_CLKDEV_CON(sys_ahb),
+	_DEFINE_CLKDEV_CON(sys_apb),
+	_DEFINE_CLKDEV_CON(lcd),
+	_DEFINE_CLKDEV_CON(apbdma_a),
+	_DEFINE_CLKDEV_CON(saacc),
+	_DEFINE_CLKDEV_CON(i2stx_mck),
+	_DEFINE_CLKDEV_CON(i2srx_mck),
+	_DEFINE_CLKDEV_CON(i2stx_bck),
+	_DEFINE_CLKDEV_CON(i2srx_bck),
+	_DEFINE_CLKDEV_DEV("uart.0", uart),
+	_DEFINE_CLKDEV_DEV("uart.1", uart),
+	_DEFINE_CLKDEV_DEV("uart.2", uart),
+	_DEFINE_CLKDEV_DEV("93000000.fb", lcd_ctrl),
+	_DEFINE_CLKDEV_DEV("90000000.pwm", tmrb),
+	_DEFINE_CLKDEV_DEV("92b00000.dma", apbdma_c),
+	_DEFINE_CLKDEV_DEV("92b0b000.mmc", sd0),
+	_DEFINE_CLKDEV_DEV("93010000.dma", apbdma_a),
+	_DEFINE_CLKDEV_DEV("93012000.i2s", i2stx_ctrl),
+	_DEFINE_CLKDEV_DEV("9301d000.i2s", i2srx_ctrl),
+};
+
+void __init spmp8000_init_clkdev(void)
+{
+	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+	/* Enable the apll control registers here, as the according clock
+	 * isn't exported, but used by the mck/bck clocks. If we wouldn't
+	 * enable it here, the freq of the pll couldn't be set up before
+	 * enabling one of its child clocks.
+	 * The PLL clock itself will be auto-enabled on demand by them.
+	 */
+	clk_enable(&clk_apll_ctrl);
+}
diff --git a/arch/arm/mach-spmp8000/clock.c b/arch/arm/mach-spmp8000/clock.c
new file mode 100644
index 0000000..5652aff
--- /dev/null
+++ b/arch/arm/mach-spmp8000/clock.c
@@ -0,0 +1,155 @@
+/*
+ * Usual clk API boilerplate
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+
+#include <mach/clock.h>
+
+static DEFINE_MUTEX(clocks_mutex);
+
+static void __clk_disable(struct clk *clk)
+{
+	BUG_ON(clk->refcount == 0);
+
+	if (!(--clk->refcount) && clk->disable) {
+		clk->disable(clk);
+		if (clk->parent)
+			__clk_disable(clk->parent);
+	}
+}
+
+static int __clk_enable(struct clk *clk)
+{
+	int ret = 0;
+
+	if (clk->refcount++ == 0 && clk->enable) {
+		if (clk->parent)
+			ret = __clk_enable(clk->parent);
+		if (ret)
+			return ret;
+		else
+			clk->enable(clk);
+	}
+
+	return 0;
+}
+
+int clk_enable(struct clk *clk)
+{
+	int ret = 0;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return -EINVAL;
+
+	mutex_lock(&clocks_mutex);
+	ret = __clk_enable(clk);
+	mutex_unlock(&clocks_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return;
+
+	mutex_lock(&clocks_mutex);
+	__clk_disable(clk);
+	mutex_unlock(&clocks_mutex);
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return 0UL;
+
+	/* Is fixed and has been calculated already */
+	if ((clk->flags & CLK_FIXED_RATE) && clk->rate)
+		return clk->rate;
+
+	if (clk->get_rate)
+		return clk->get_rate(clk);
+
+	return clk_get_rate(clk->parent);
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return 0;
+	if (unlikely(!clk->round_rate))
+		return 0;
+
+	return clk->round_rate(clk, rate);
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	int ret = -EINVAL;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return ret;
+
+	if (unlikely(clk->flags & CLK_FIXED_RATE))
+		return ret;
+
+	if (unlikely(!clk->set_rate || !rate))
+		return ret;
+
+	mutex_lock(&clocks_mutex);
+	ret = clk->set_rate(clk, rate);
+	mutex_unlock(&clocks_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk *old;
+	int ret = -EINVAL;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return ret;
+	if (unlikely(!clk->set_parent || !parent))
+		return ret;
+
+	mutex_lock(&clocks_mutex);
+	old = clk->parent;
+	if (clk->refcount)
+		__clk_enable(parent);
+	ret = clk->set_parent(clk, parent);
+	if (ret)
+		old = parent;
+	if (clk->refcount)
+		__clk_disable(old);
+	mutex_unlock(&clocks_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return NULL;
+
+	return clk->parent;
+}
+EXPORT_SYMBOL_GPL(clk_get_parent);
diff --git a/arch/arm/mach-spmp8000/include/mach/clock.h b/arch/arm/mach-spmp8000/include/mach/clock.h
new file mode 100644
index 0000000..e5cdbd2
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/clock.h
@@ -0,0 +1,37 @@
+/*
+ * SPMP8000 machines clock support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef __MACH_SPMP8000_CLOCK_H__
+#define __MACH_SPMP8000_CLOCK_H__
+
+struct clk {
+	char		*name;
+	unsigned long	rate;
+	unsigned int	id;
+	unsigned int	refcount;
+	int		scu;
+	int		scu_periph_id;
+	int		dividerreg;
+	int		divmask;
+	unsigned long	flags;
+/* Either really fixed rate (crystal) or which we don't change for sure */
+#define CLK_FIXED_RATE			BIT(0)
+/* SPMP8000 has two types of dividers, one of them needs enabling */
+#define CLK_DIVIDER_WITH_ENABLE		BIT(1)
+
+	struct clk	*parent;
+	unsigned long	(*get_rate)(struct clk *clk);
+	unsigned long	(*round_rate) (struct clk *, u32);
+	int		(*set_rate) (struct clk *, unsigned long);
+	int		(*set_parent) (struct clk *clk, struct clk *parent);
+	void		(*enable) (struct clk *);
+	void		(*disable) (struct clk *);
+};
+
+#endif /* __MACH_SPMP8000_CLOCK_H__ */
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 4/9] ARM: SPMP8000: Add ADC driver
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/mach-spmp8000/adc.c                      |  465 +++++++++++++++++++++
 arch/arm/mach-spmp8000/include/mach/spmp8000adc.h |   29 ++
 2 files changed, 494 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/adc.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/spmp8000adc.h

diff --git a/arch/arm/mach-spmp8000/adc.c b/arch/arm/mach-spmp8000/adc.c
new file mode 100644
index 0000000..c517116
--- /dev/null
+++ b/arch/arm/mach-spmp8000/adc.c
@@ -0,0 +1,465 @@
+/*
+ * SPMP8000 ADC support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <mach/spmp8000adc.h>
+#include <mach/scu.h>
+
+#define SARCTRL			0x00
+#define SARCTRL_BUSY		BIT(21)
+#define SARCTRL_REF		BIT(17)
+#define SARCTRL_TOG_EN		BIT(16)
+#define SARCTRL_DIVNUM_OFF	8
+#define SARCTRL_DIVNUM_MASK	0xFF00
+#define SARCTRL_BACK2INT	BIT(7)
+#define SARCTRL_TPS_OFF		5
+#define SARCTRL_TPS_MASK	(3 << 5)
+#define SARCTRL_TPS_NOP		0
+#define SARCTRL_TPS_TEST	1
+#define SARCTRL_TPS_TP		2
+#define SARCTRL_TPS_INT		3
+#define SARCTRL_SARS_OFF	2
+#define SARCTRL_SARS_MASK	(7 << 2)
+#define SARCTRL_SARS_TPXP	0
+#define SARCTRL_SARS_TPXN	1
+#define SARCTRL_SARS_TPYP	2
+#define SARCTRL_SARS_TPYN	3
+#define SARCTRL_SARS_SARIN4	4
+#define SARCTRL_SARS_SARIN5	5
+#define SARCTRL_SARS_SARIN6	6
+#define SARCTRL_SARS_SARIN7	7
+#define SARCTRL_MANCON		BIT(1)
+#define SARCTRL_AUTOCON		BIT(0)
+
+#define SARCONDLY		0x04
+#define SARAUTODLY		0x08
+#define SARDEBTIME		0x0C
+#define SARDEBTIME_DEBDLY_OFF	0
+#define SARDEBTIME_DEBDLY_MASK	0xFFFF
+#define SARDEBTIME_CHKDLY_OFF	16
+#define SARDEBTIME_CHKDLY_MASK	0xFFFF0000UL
+#define SARPNL			0x10
+#define SARAUX			0x14
+#define SARINTEN		0x18
+#define SARINTF			0x1C
+#define SARINT_NAUX		BIT(3)
+#define SARINT_NPNL		BIT(2)
+#define SARINT_PENUP		BIT(1)
+#define SARINT_PENDOWN		BIT(0)
+
+#define SAR_CLKRATE		400000UL
+#define ADC_CHANNELS		8
+
+struct spmp8000_adc {
+	void __iomem		*reg_base;
+	struct device		*dev;
+	struct clk		*clk_apbdma;
+	struct clk		*clk_saacc;
+	spinlock_t		lock;
+	/* These are callbacks that get served with the measurement data
+	 *@their according channel interrupt. */
+	struct spmp8000adc_client_chan	*chan[ADC_CHANNELS];
+	/* Same for the touch-panel data */
+	struct spmp8000adc_client_tp	*tp;
+};
+
+static struct spmp8000_adc	*adc_dev;
+
+static int spmp8000adc_should_run(struct spmp8000_adc *adc)
+{
+	int i = ADC_CHANNELS - 1;
+
+	while (i-- >= 0) {
+		if (adc->chan[i])
+			return 1;
+	}
+
+	return 0;
+}
+
+/* Starts or stops the ADC controller */
+static void spmp8000adc_ctrl(struct spmp8000_adc *adc, int start, int chan)
+{
+	u32			ctrl_reg;
+
+	ctrl_reg = readl(adc->reg_base + SARCTRL);
+
+	if (start) {
+		ctrl_reg &= ~SARCTRL_SARS_MASK;
+		ctrl_reg |= (chan << SARCTRL_SARS_OFF);
+		ctrl_reg |= (SARCTRL_TPS_TEST << SARCTRL_TPS_OFF) |
+				SARCTRL_MANCON;
+	} else
+		ctrl_reg &= ~SARCTRL_MANCON;
+
+	writel(ctrl_reg, adc->reg_base + SARCTRL);
+}
+
+/* Request an ADC channel to be measured. Measurement starts automatically
+ * when at least one channel is requested. IRQ code loops around all channels
+ * to be measured.
+ */
+int spmp8000adc_request_channel(unsigned int chan,
+				struct spmp8000adc_client_chan *c)
+{
+	struct spmp8000_adc	*adc = adc_dev;
+	unsigned long		flags;
+
+	if (chan > ADC_CHANNELS - 1)
+		return -EINVAL;
+
+	if (!c)
+		return -EINVAL;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	if (adc->chan[chan]) {
+		spin_unlock_irqrestore(&adc->lock, flags);
+		return -EBUSY;
+	}
+
+	adc->chan[chan] = c;
+
+	if (spmp8000adc_should_run(adc))
+		spmp8000adc_ctrl(adc, 1, chan);
+
+	spin_unlock_irqrestore(&adc->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(spmp8000adc_request_channel);
+
+/* Release an ADC channel. If no more channels are to be measured,
+ * the controller is shut down
+ */
+int spmp8000adc_release_channel(unsigned int chan)
+{
+	struct spmp8000_adc	*adc = adc_dev;
+	unsigned long		flags;
+
+	if (chan > ADC_CHANNELS - 1)
+		return -EINVAL;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	if (!spmp8000adc_should_run(adc))
+		spmp8000adc_ctrl(adc, 0, 0);
+
+	adc->chan[chan] = NULL;
+
+	spin_unlock_irqrestore(&adc->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(spmp8000adc_release_channel);
+
+/* Request touch panel measurement */
+int spmp8000adc_request_tp(struct spmp8000adc_client_tp *tp)
+{
+	struct spmp8000_adc	*adc = adc_dev;
+	unsigned long		flags;
+	u32 ctrl_reg;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	if (adc->tp) {
+		spin_unlock_irqrestore(&adc->lock, flags);
+		return -EBUSY;
+	}
+
+	adc->tp = tp;
+
+	ctrl_reg = readl(adc->reg_base + SARCTRL);
+	ctrl_reg |= SARCTRL_AUTOCON;
+	writel(ctrl_reg, adc->reg_base + SARCTRL);
+	spin_unlock_irqrestore(&adc->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(spmp8000adc_request_tp);
+
+/* Relase touch panel measurement */
+void spmp8000adc_release_tp(void)
+{
+	struct spmp8000_adc	*adc = adc_dev;
+	unsigned long		flags;
+	u32 ctrl_reg;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	ctrl_reg = readl(adc->reg_base + SARCTRL);
+	ctrl_reg &= ~SARCTRL_AUTOCON;
+	writel(ctrl_reg, adc->reg_base + SARCTRL);
+	adc->tp = NULL;
+
+	spin_unlock_irqrestore(&adc->lock, flags);
+}
+EXPORT_SYMBOL(spmp8000adc_release_tp);
+
+static irqreturn_t spmp8000adc_irq(int irq, void *dev)
+{
+	struct spmp8000_adc		*adc = dev;
+	void __iomem			*regs = adc->reg_base;
+	u32				ints, ctrl;
+	s32				val_tp;
+	s16				val_chan;
+	int curchan;
+
+	ints = readl(regs + SARINTF);
+
+	dev_vdbg(adc->dev, "irq: %08X\n", ints);
+
+	if (unlikely(!ints))
+		return IRQ_NONE;
+
+	if (unlikely(ints & SARINT_PENDOWN))
+		dev_dbg(adc->dev, "IRQ: Pen down\n");
+
+	if (unlikely(ints & SARINT_PENUP))
+		dev_dbg(adc->dev, "IRQ: Pen up\n");
+
+	/* Channel measurement end in manual mode */
+	if (ints & SARINT_NAUX) {
+		spin_lock(&adc->lock);
+
+		ctrl = readl(regs + SARCTRL);
+		curchan = (ctrl & SARCTRL_SARS_MASK) >> SARCTRL_SARS_OFF;
+		dev_vdbg(adc->dev, "irq aux chan: %d\n", curchan);
+
+		/* The read also clears the interrupt */
+		val_chan = readl(regs + SARAUX);
+		if (adc->chan[curchan])
+			adc->chan[curchan]->cb(val_chan,
+						adc->chan[curchan]->priv);
+
+		if (!spmp8000adc_should_run(adc)) {
+			spin_unlock(&adc->lock);
+			return IRQ_HANDLED;
+		}
+
+		/* Select next channel */
+		curchan++;
+		if (curchan == ADC_CHANNELS - 1)
+			curchan = 0;
+
+		/* Set up next measurement */
+		spmp8000adc_ctrl(adc, 1, curchan);
+
+		spin_unlock(&adc->lock);
+	}
+
+	/* Touch panel measurement end in automatic mode */
+	if (ints & SARINT_NPNL) {
+		/* The read also clears the interrupt */
+		val_tp = readl(regs + SARPNL);
+		if (adc->tp)
+			adc->tp->cb(val_tp, adc->tp->priv);
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+static int __devinit spmp8000adc_probe(struct platform_device *pdev)
+{
+	struct spmp8000_adc		*adc;
+	struct clk			*clk_apbdma, *clk_saacc;
+	unsigned long			clkrate;
+	struct resource			*r;
+	int ret, irq;
+
+	if (adc_dev) {
+		dev_err(&pdev->dev, "ADC driver already initialized\n");
+		return -EINVAL;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r)
+		return -ENODEV;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (!r) {
+		dev_err(&pdev->dev, "Failed to request base memory region\n");
+		return -EBUSY;
+	}
+
+	adc = kzalloc(sizeof(struct spmp8000_adc), GFP_KERNEL);
+	if (!adc) {
+		ret = -ENOMEM;
+		goto out_release_mem;
+	}
+
+	adc->reg_base = ioremap(r->start, resource_size(r));
+	if (!adc->reg_base) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	clk_apbdma = clk_get(&pdev->dev, "apbdma_a");
+	if (IS_ERR_OR_NULL(clk_apbdma)) {
+		dev_err(&pdev->dev, "Can't get data path clock\n");
+		ret = -ENODEV;
+		goto out_iounmap;
+	}
+
+	clk_saacc = clk_get(&pdev->dev, "saacc");
+	if (IS_ERR_OR_NULL(clk_saacc)) {
+		dev_err(&pdev->dev, "Can't get controller clock\n");
+		ret = -ENODEV;
+		goto out_put_clk_apbdma;
+	}
+
+	clk_enable(clk_apbdma);
+	clk_enable(clk_saacc);
+
+	clkrate = clk_get_rate(clk_saacc);
+
+	clkrate = DIV_ROUND_UP(clkrate, SAR_CLKRATE);	/* Target clkrate */
+	clkrate = DIV_ROUND_UP(clkrate, 2) - 1;		/* Divisor */
+
+	dev_dbg(&pdev->dev, "clock rate: %ld\n", clk_get_rate(clk_saacc) /
+		((clkrate + 1) * 2));
+
+	if (clkrate > 255) {
+		dev_err(&pdev->dev, "Input clockrate too high\n");
+		ret = -EINVAL;
+		goto out_clk_disable;
+	}
+
+	spin_lock_init(&adc->lock);
+	adc->dev = &pdev->dev;
+	adc->clk_apbdma = clk_apbdma;
+	adc->clk_saacc = clk_saacc;
+
+	ret = request_irq(irq, spmp8000adc_irq, 0, dev_name(&pdev->dev), adc);
+	if (ret)
+		goto out_clk_disable;
+
+	adc_dev = adc;
+	platform_set_drvdata(pdev, adc);
+
+	/* Set internal clock divider */
+	writel(clkrate << SARCTRL_DIVNUM_OFF, adc->reg_base + SARCTRL);
+
+	/* Set conversion time defaults */
+	writel(0x10010, adc->reg_base + SARAUTODLY);
+	writel(0x1000, adc->reg_base + SARDEBTIME);
+	writel(0xFF, adc->reg_base + SARCONDLY);
+
+	/* Clear pending interrupts */
+	writel(0xF, adc->reg_base + SARINTF);
+
+	/* Enable interrupts */
+	writel(0xF, adc->reg_base + SARINTEN);
+
+	dev_info(&pdev->dev, "registered\n");
+
+	return 0;
+
+out_clk_disable:
+	clk_disable(clk_saacc);
+	clk_disable(clk_apbdma);
+	clk_put(clk_saacc);
+out_put_clk_apbdma:
+	clk_put(clk_apbdma);
+out_iounmap:
+	iounmap(adc->reg_base);
+out_free:
+	kfree(adc);
+out_release_mem:
+	release_mem_region(r->start, resource_size(r));
+
+	return ret;
+}
+
+static int __devexit spmp8000adc_remove(struct platform_device *pdev)
+{
+	struct spmp8000_adc		*adc;
+	struct resource			*r;
+	int irq;
+
+	adc = platform_get_drvdata(pdev);
+
+	/* Disable and clear remaining interrupts */
+	writel(0, adc->reg_base + SARCTRL);
+	writel(0xF, adc->reg_base + SARINTEN);
+	writel(0xF, adc->reg_base + SARINTF);
+
+	clk_disable(adc->clk_saacc);
+	clk_disable(adc->clk_apbdma);
+	clk_put(adc->clk_saacc);
+	clk_put(adc->clk_apbdma);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	free_irq(irq, adc);
+
+	iounmap(adc->reg_base);
+
+	kfree(adc);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r)
+		return -ENODEV;
+
+	release_mem_region(r->start, resource_size(r));
+
+	platform_set_drvdata(pdev, NULL);
+
+	dev_info(&pdev->dev, "registered\n");
+
+	return 0;
+}
+
+static const struct of_device_id spmp8000_adc_match[] __devinitdata = {
+	{ .compatible = "sunplus,spmp8000-adc" },
+	{},
+};
+
+static struct platform_driver spmp8000adc_driver = {
+	.driver		= {
+		.name	= "spmp8000-adc",
+		.owner	= THIS_MODULE,
+		.of_match_table = spmp8000_adc_match,
+	},
+	.probe		= spmp8000adc_probe,
+	.remove		= __devexit_p(spmp8000adc_remove),
+};
+
+
+static int __init spmp8000adc_init(void)
+{
+	return platform_driver_register(&spmp8000adc_driver);
+}
+module_init(spmp8000adc_init);
+
+static void __exit spmp8000adc_exit(void)
+{
+	platform_driver_unregister(&spmp8000adc_driver);
+}
+module_exit(spmp8000adc_exit);
+
+MODULE_AUTHOR("Zoltan Devai <zoss@devai.org>");
+MODULE_DESCRIPTION("ADC driver for Sunplus SPMP8000 SoC");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-spmp8000/include/mach/spmp8000adc.h b/arch/arm/mach-spmp8000/include/mach/spmp8000adc.h
new file mode 100644
index 0000000..df5dfad
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/spmp8000adc.h
@@ -0,0 +1,29 @@
+/*
+ * SPMP8000 ADC support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef __MACH_SPMP8000_ADC_H__
+#define __MACH_SPMP8000_ADC_H__
+
+struct spmp8000adc_client_chan {
+	void	(*cb)(s16, void*); /* Callback for every measurement value */
+	void	*priv;		/* Private data for client passed to cb */
+};
+
+struct spmp8000adc_client_tp {
+	void	(*cb)(s32, void*); /* Callback for every measurement value */
+	void	*priv;		/* Private data for client passed to cb */
+};
+
+extern int spmp8000adc_request_channel(unsigned int chan,
+				struct spmp8000adc_client_chan *c);
+extern int spmp8000adc_release_channel(unsigned int chan);
+extern int spmp8000adc_request_tp(struct spmp8000adc_client_tp *tp);
+extern void spmp8000adc_release_tp(void);
+
+#endif /* __MACH_SPMP8000_ADC_H__ */
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 5/9] ARM: SPMP8000: Add pinmux driver
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/mach-spmp8000/pinmux.c |  131 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 131 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/pinmux.c

diff --git a/arch/arm/mach-spmp8000/pinmux.c b/arch/arm/mach-spmp8000/pinmux.c
new file mode 100644
index 0000000..61174d5
--- /dev/null
+++ b/arch/arm/mach-spmp8000/pinmux.c
@@ -0,0 +1,131 @@
+/*
+ * SPMP8000 machines pinmux functions
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+#include <mach/scu.h>
+
+static int pgc_to_regs(int pc, void __iomem **reg, int *off, int *mask_bits)
+{
+	if (pc > 52) {
+		pr_err("Invalid pad group control number\n");
+		return -EINVAL;
+	}
+
+	*mask_bits = 1;
+
+	/* The pin controls are nicely scattered around */
+	if (pc < 10) {
+		*reg = REG_SCU_B(SCU_B_PGC0);
+		*off = pc * 3;
+		*mask_bits = 3;
+	} else if (pc == 10) {
+		*reg = REG_SCU_B(SCU_B_PGC1);
+		*off = 0;
+	} else if (pc < 42) {
+		*reg = REG_SCU_B(SCU_B_PGC2);
+		*off = pc - 10;
+	} else {
+		*reg = REG_SCU_B(SCU_B_PGC3);
+		*off = pc - 42;
+	}
+
+	return 0;
+}
+
+/**
+ * Set the control function (usually pull-up/down) of a pin group
+ * @pc Pin control group number
+ * @conf Control function state
+ */
+void spmp8000_pinmux_pgc_set(int pc, unsigned int conf)
+{
+	void __iomem *pgc_reg;
+	int ret, pgc_off, mask_bits;
+	u32 reg;
+
+	ret = pgc_to_regs(pc, &pgc_reg, &pgc_off, &mask_bits);
+	if (ret)
+		return;
+
+	pr_debug("pgc_set: %d, reg: %p, off: %d, mask: %d\n", pc, pgc_reg,
+		pgc_off, mask_bits);
+
+	reg = readl(pgc_reg);
+	reg &= ~(((1 <<  mask_bits) - 1) << pgc_off);
+	reg |= (conf << pgc_off);
+	writel(reg, pgc_reg);
+}
+
+/**
+ * Get the control function (usually pull-up/down) of a pin group
+ * @pc Pin control group number
+ * @returns Current function control state
+ */
+unsigned int spmp8000_pinmux_pgc_get(int pc)
+{
+	void __iomem *pgc_reg;
+	int ret, pgc_off, mask_bits;
+	u32 reg;
+
+	ret = pgc_to_regs(pc, &pgc_reg, &pgc_off, &mask_bits);
+	if (ret)
+		return ret;
+
+	reg = readl(pgc_reg);
+	reg >>= pgc_off;
+	reg &= (1 <<  mask_bits) - 1;
+	return reg;
+}
+
+static inline int pgs_to_reg(unsigned int pg)
+{
+	return (pg / 16) * 4;
+}
+
+static inline int pgs_to_off(unsigned int pg)
+{
+	return (pg % 16) * 2;
+}
+
+/**
+ * Set the function of a pin group
+ * @pg Pin group
+ * @func Function
+ */
+void spmp8000_pinmux_pgs_set(unsigned int pg, unsigned int func)
+{
+	u32 reg;
+
+	if ((pg > 63) || (func > 3)) {
+		pr_err("Invalid pad group function select\n");
+		return;
+	}
+
+	reg = readl(REG_SCU_B(SCU_B_PGS0 + pgs_to_reg(pg)));
+	reg &= ~(3 << pgs_to_off(pg));
+	reg |= (func << pgs_to_off(pg));
+	writel(reg, REG_SCU_B(SCU_B_PGS0 + pgs_to_reg(pg)));
+}
+/**
+ * Get the current function associated to the pin group
+ * @pg Pin group
+ * @returns The current function of the pin group
+ */
+int spmp8000_pinmux_pgs_get(int pg)
+{
+	u32 reg;
+
+	reg = readl(REG_SCU_B(SCU_B_PGS0 + pgs_to_reg(pg)));
+	reg = (reg >> pgs_to_off(pg)) & 3;
+	return reg;
+}
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 6/9] ARM: SPMP8000: Add pwm driver
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/mach-spmp8000/pwm.c |  246 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 246 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/pwm.c

diff --git a/arch/arm/mach-spmp8000/pwm.c b/arch/arm/mach-spmp8000/pwm.c
new file mode 100644
index 0000000..a6418bf
--- /dev/null
+++ b/arch/arm/mach-spmp8000/pwm.c
@@ -0,0 +1,246 @@
+/*
+ * SPMP8000 machines PWM support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/div64.h>
+
+#include <mach/regs-timer.h>
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device {
+	const char	*label;
+	void __iomem	*regbase;
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+	unsigned long	clkrate;
+	struct device	*dev;
+	struct list_head	node;
+};
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, duty_cycles, prescale;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = pwm->clkrate;
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	prescale = (period_cycles - 1) / 65536;
+
+	if (prescale > 65535)
+		return -EINVAL;
+
+	if (prescale < 1)
+		prescale = 1;
+
+	period_cycles /= prescale;
+
+	if (period_cycles > 65536)
+		return -EINVAL;
+
+	c = (unsigned long long)period_cycles * duty_ns;
+	do_div(c, period_ns);
+	duty_cycles = c;
+
+	dev_dbg(pwm->dev, "period_ns: %d, duty_ns: %d, prescale: %ld,"
+			  " period: %ld, duty: %ld\n",
+			  period_ns, duty_ns, prescale, period_cycles,
+			  duty_cycles);
+
+	writel(prescale - 1, pwm->regbase + SPMP8000_TMRB_PSR);
+	writel(period_cycles, pwm->regbase + SPMP8000_TMRB_LDR);
+	writel(duty_cycles, pwm->regbase + SPMP8000_TMRB_CMP);
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_OE |
+		SPMP8000_TMRB_CTR_PWMON | SPMP8000_TMRB_CTR_UD |
+		(1 << SPMP8000_TMRB_CTR_M_SH),
+		pwm->regbase + SPMP8000_TMRB_CTR);
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	writel(0, pwm->regbase + SPMP8000_TMRB_CTR);
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else {
+			pwm = ERR_PTR(-EBUSY);
+		}
+	} else {
+		pwm = ERR_PTR(-ENOENT);
+	}
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else {
+		pr_warning("PWM device already freed\n");
+	}
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const unsigned int *id;
+	struct pwm_device *pwm;
+	void __iomem *base;
+	struct clk *clk;
+
+	id = of_get_property(np, "id", NULL);
+	if (!id) {
+		dev_dbg(&pdev->dev, "No id specified\n");
+		return -ENODEV;
+	}
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		dev_dbg(&pdev->dev, "Can't map registers");
+		return -ENOMEM;
+	}
+
+	clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_dbg(&pdev->dev, "Unable to get pwm device clock");
+		return -ENODEV;
+	}
+
+	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+	if (pwm == NULL) {
+		dev_dbg(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	pwm->dev = &pdev->dev;
+	pwm->use_count = 0;
+	pwm->pwm_id = *id;
+	pwm->regbase = base;
+	pwm->clkrate = clk_get_rate(clk);
+
+	dev_dbg(&pdev->dev, "register pwm.%d, regbase %p, clkrate: %ld\n",
+		pwm->pwm_id, pwm->regbase, pwm->clkrate);
+
+	dev_info(&pdev->dev, "pwm %d registered\n", pwm->pwm_id);
+
+	__add_pwm(pwm);
+
+	platform_set_drvdata(pdev, pwm);
+	return 0;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwm;
+
+	pwm = platform_get_drvdata(pdev);
+	if (pwm == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+	list_del(&pwm->node);
+	mutex_unlock(&pwm_lock);
+
+	kfree(pwm);
+	return 0;
+}
+
+static const __devinitconst struct of_device_id spmp8000_pwm_match[] = {
+	{
+		.compatible = "sunplus,spmp8000-pwm",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, spmp8000_pwm_match);
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "spmp8000-pwm",
+		.owner	= THIS_MODULE,
+		.of_match_table = spmp8000_pwm_match,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_AUTHOR("Zoltan Devai <zoss@devai.org>");
+MODULE_LICENSE("GPL");
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 7/9] ARM: SPMP8000: Add dts file of SPMP8000 SoC and Letcool board
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/boot/dts/spmp8000-letcool.dts |  163 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/spmp8000.dtsi        |  169 ++++++++++++++++++++++++++++++++
 2 files changed, 332 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/dts/spmp8000-letcool.dts
 create mode 100644 arch/arm/boot/dts/spmp8000.dtsi

diff --git a/arch/arm/boot/dts/spmp8000-letcool.dts b/arch/arm/boot/dts/spmp8000-letcool.dts
new file mode 100644
index 0000000..270f00e
--- /dev/null
+++ b/arch/arm/boot/dts/spmp8000-letcool.dts
@@ -0,0 +1,163 @@
+/dts-v1/;
+/include/ "spmp8000.dtsi"
+/ {
+	model = "Letcool N350JP handheld game console";
+	compatible = "gameware,letcool", "sunplus,spmp8000";
+
+	memory {
+		reg = <0x00000000 0x02000000>;
+	};
+
+	chosen {
+		bootargs = "console=tty0 mem=32M root=/dev/mmcblk0p1 console=ttyS0,115200 rootdelay=5 loglevel=8 earlyprintk";
+		linux,stdout-path = &uart0;
+	};
+
+	pwm-backlight {
+		compatible = "pwm-backlight";
+		reg = <0 0>;	/* Hack to pass platform data as AUXDATA */
+	};
+
+	plat {
+		plat-apb {
+			mmc at 92B0B000 {
+				cd-gpios = <&gpio0 9 0>;
+			};
+		};
+	};
+
+	armapb {
+		gpio at 9000A000 {
+				polarity = <0>;
+				sticky = <0>;
+				direction = <0xFFFFFFFF>;
+				debounce-val = <100>;
+				debounce = <0xFFFFFFFF>;
+		};
+	
+		gpio at 9000A004 {
+				polarity = <0>;
+				sticky = <0>;
+				direction = <0xFFFFFFFF>;
+				debounce-val = <100>;
+				debounce = <0xFFFFFFFF>;
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		autorepeat;
+
+		up {
+			label = "D-Pad Up";
+			gpios = <&gpio0 0 0>;
+			linux,code = <103>; /* KEY_UP */
+			gpio-key,wakeup;
+		};
+
+		right {
+			label = "D-Pad Right";
+			gpios = <&gpio0 1 0>;
+			linux,code = <106>; /* KEY_RIGHT */
+			gpio-key,wakeup;
+		};
+
+		down {
+			label = "D-Pad Down";
+			gpios = <&gpio0 2 0>;
+			linux,code = <108>; /* KEY_DOWN */
+			gpio-key,wakeup;
+		};
+
+		left {
+			label = "D-Pad Left";
+			gpios = <&gpio0 3 0>;
+			linux,code = <105>; /* KEY_LEFT */
+			gpio-key,wakeup;
+		};
+
+		button-x {
+			label = "Button X";
+			gpios = <&gpio0 4 0>;
+			linux,code = <45>; /* KEY_X */
+			gpio-key,wakeup;
+		};
+
+		button-a {
+			label = "Button A";
+			gpios = <&gpio0 5 0>;
+			linux,code = <30>; /* KEY_A */
+			gpio-key,wakeup;
+		};
+
+		button-y {
+			label = "Button Y";
+			gpios = <&gpio0 6 0>;
+			linux,code = <21>; /* KEY_Y */
+			gpio-key,wakeup;
+		};
+
+		button-b {
+			label = "Button B";
+			gpios = <&gpio0 7 0>;
+			linux,code = <48>; /* KEY_B */
+			gpio-key,wakeup;
+		};
+	};
+
+	analog-keys {
+		compatible = "spmp8000-ak";
+
+		channel-1 {
+			adc-channel = <1>;
+			idle-value = <0>;
+			tolerance = <10>;
+
+			button-left {
+				adc-value = <0x00B0>;
+				linux,code = <0x110>; /* BTN_LEFT */
+			};
+
+			button-right {
+				adc-value = <0xFEAB>;
+				linux,code = <0x111>; /* BTN_RIGHT */
+			};
+
+			button-volup {
+				adc-value = <0x015E>;
+				linux,code = <115>; /* KEY_VOLUMEUP */
+			};
+
+			button-voldown {
+				adc-value = <0xFF56>;
+				linux,code = <114>; /* KEY_VOLUMEDOWN */
+			};
+		};
+
+		channel-3 {
+			adc-channel = <3>;
+			idle-value = <5>;
+			tolerance = <10>;
+
+			button-select {
+				adc-value = <0x0113>;
+				linux,code = <0x13a>; /* BTN_SELECT */
+			};
+
+			button-start {
+				adc-value = <0xFEF7>;
+				linux,code = <0x13b>; /* BTN_START */
+			};
+		};
+
+	};
+
+	snd-card {
+		compatible = "spmp8000-sndcard";
+		hp-detect-gpios = <&gpio0 8 0>;
+		spk-enable-gpios = <&gpio3 12 0>;
+	};
+
+};
diff --git a/arch/arm/boot/dts/spmp8000.dtsi b/arch/arm/boot/dts/spmp8000.dtsi
new file mode 100644
index 0000000..74764b2c6
--- /dev/null
+++ b/arch/arm/boot/dts/spmp8000.dtsi
@@ -0,0 +1,169 @@
+/ {
+	compatible = "sunplus,spmp8000";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	armapb {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x90000000 0x90000000 0x10000>;
+
+		timer at 90000000 {
+			compatible = "sunplus,spmp8000-timer";
+			reg = <0x90000000 0x1000>;
+			interrupt-parent = <&vic0>;
+			interrupts = <7 8 9>;
+		};
+
+		pwm at 90000000 {
+			compatible = "sunplus,spmp8000-pwm";
+			reg = <0x90000000 0x1000>;
+			id = <0>;
+		};
+
+		gpio3: gpio at 90005070 {
+			compatible = "basic-mmio-gpio";	/* No DT binding yet */
+			reg = <0x90005070 0xC>;
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		gpio0: gpio at 9000A000 {
+			compatible = "sunplus,spmp8000-gpio";
+			reg = <0x9000A000 0x1000>;
+			interrupt-parent = <&vic1>;
+			interrupts = <0>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			bits = <16>;
+			interrupt-controller;
+			#interrupt-cells = <1>;
+		};
+
+		gpio1: gpio at 9000A004 {
+			compatible = "sunplus,spmp8000-gpio";
+			reg = <0x9000A004 0x1000>;
+			interrupt-parent = <&vic1>;
+			interrupts = <1>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			bits = <32>;
+			interrupt-controller;
+			#interrupt-cells = <1>;
+		};
+	};
+
+	armahb {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x90010000 0x90010000 0x20000>;
+
+		vic0: interrupt-controller at 90010000 {
+			compatible = "arm,pl192";
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			reg = <0x90010000 0x1000>;
+		};
+
+		vic1: interrupt-controller at 900020000 {
+			compatible = "arm,pl192";
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			reg = <0x90020000 0x1000>;
+		};
+	};
+
+	plat {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x92000000 0x92000000 0x1000000>;
+
+		plat-apb {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x92B00000 0x92B00000 0x10000>;
+
+			dma at 92B00000 {
+				compatible = "sunplus,spmp8000-apbdma-c";
+				reg = <0x92B00000 0x1000>;
+				interrupt-parent = <&vic1>;
+				interrupts = <28 29 30 31>;
+			};
+
+			uart0: uart-c0 at 92B04000 {
+				compatible = "ns16550a";
+				reg = <0x92B04000 0x1000>;
+				clock-frequency = <2076923>;
+				interrupt-parent = <&vic1>;
+				interrupts = <24>;
+				current-speed = <115200>;
+				reg-shift = <2>;
+			};
+
+			mmc at 92B0B000 {
+				compatible = "sunplus,mmc";
+				reg = <0x92B0B000 0x1000>;
+				interrupt-parent = <&vic1>;
+				interrupts = <8>;
+			};
+		};
+	};
+
+	axi {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x93000000 0x93000000 0x800000>;
+
+		fb at 93000000 {
+			compatible = "sunplus,spmp8000-lcd";
+			reg = <0x93000000 0x1000>;
+			interrupt-parent = <&vic0>;
+			interrupts = <23>;
+		};
+
+		axi-apb {
+			compatible = "simple-bus";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x93010000 0x93010000 0x10000>;
+
+			dma at 93010000 {
+				compatible = "sunplus,spmp8000-apbdma-a";
+				reg = <0x93010000 0x1000>;
+				interrupt-parent = <&vic0>;
+				interrupts = <24 25 26 27>;
+			};
+
+			i2s at 93012000 {
+				compatible = "sunplus,spmp8000-i2s-tx";
+				reg = <0x93012000 0x1000>;
+				interrupt-parent = <&vic0>;
+				interrupts = <13>;
+			};
+
+			i2s at 9301D000 {
+				compatible = "sunplus,spmp8000-i2s-rx";
+				reg = <0x9301D000 0x1000>;
+				interrupt-parent = <&vic0>;
+				interrupts = <14>;
+			};
+
+			adc at 9301F000 {
+				compatible = "sunplus,spmp8000-adc";
+				reg = <0x9301F000 0x20>;
+				interrupt-parent = <&vic1>;
+				interrupts = <27>;
+			};
+
+			codec at 9301F020 {
+				compatible = "sunplus,spmp8000-codec";
+				reg = <0x9301F020 0x20>;
+			};
+		};
+	};
+};
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 8/9] ARM: SPMP8000: Add support for the Letcool board
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/mach-spmp8000/board_letcool.c           |  154 ++++++++++++++++++++++
 arch/arm/mach-spmp8000/include/mach/spmp8000fb.h |   32 +++++
 2 files changed, 186 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/board_letcool.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/spmp8000fb.h

diff --git a/arch/arm/mach-spmp8000/board_letcool.c b/arch/arm/mach-spmp8000/board_letcool.c
new file mode 100644
index 0000000..8d6c081
--- /dev/null
+++ b/arch/arm/mach-spmp8000/board_letcool.c
@@ -0,0 +1,154 @@
+/*
+ * Letcool board support file
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/input.h>
+#include <linux/basic_mmio_gpio.h>
+#include <linux/gpio_keys.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <asm/hardware/vic.h>
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include <mach/core.h>
+#include <mach/gpio.h>
+#include <mach/scu.h>
+#include <mach/spmp8000fb.h>
+
+/* Backlight */
+static struct platform_pwm_backlight_data backlight_letcool = {
+	.pwm_id		= 0,
+	.max_brightness	= 100,
+	.dft_brightness	= 20,
+	.pwm_period_ns	= 100000,
+};
+
+/* Framebuffer */
+static struct spmp8000fb_pdata spmp8000fb_letcool = {
+	.data_fmt = TFT_FMT_RGB565,
+	.mode = {
+		.name = "A035QN02V6",
+		.refresh = 60,
+		.xres = 320,
+		.yres = 240,
+		.pixclock = KHZ2PICOS(5000),
+		.left_margin = 8,
+		.right_margin = 8,
+		.upper_margin = 2,
+		.lower_margin = 2,
+		.hsync_len = 1,
+		.vsync_len = 1,
+	},
+};
+
+/* GPIO3 */
+static struct resource spmp8000_gpio3_resources[] = {
+	[0] = {
+		.name = "dat",
+		.start = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_I,
+		.end = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_I + 3,
+		.flags = IORESOURCE_MEM,
+	},
+	[1] = {
+		.name = "set",
+		.start = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_O,
+		.end = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_O + 3,
+		.flags = IORESOURCE_MEM,
+	},
+	[2] = {
+		.name = "dirin",
+		.start = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_E,
+		.end = SPMP8000_SCU_B_BASE + SCU_B_GPIO3_E + 3,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+static struct of_dev_auxdata letcool_auxdata_lookup[] __initdata = {
+	OF_DEV_AUXDATA("pwm-backlight", 0, "pwm-backlight",
+			&backlight_letcool),
+	OF_DEV_AUXDATA("sunplus,spmp8000-lcd", 0x93000000, "93000000.fb",
+			&spmp8000fb_letcool),
+	OF_DEV_AUXDATA("basic-mmio-gpio", 0x90005070, "basic-mmio-gpio",
+			NULL),
+	{ /* sentinel */ },
+};
+
+static void __init letcool_dt_init(void)
+{
+	struct device_node *gpio3_np;
+	struct platform_device *gpio3_pdev;
+
+	/* Static board setup stuff */
+
+	/* Switch off pull-down on SD Card detect pin */
+	spmp8000_pinmux_pgc_set(47, 0);
+
+	/* Set GPIO0 pins as GPIO */
+	spmp8000_pinmux_pgs_set(31, 2);
+	spmp8000_pinmux_pgs_set(32, 2);
+	spmp8000_pinmux_pgs_set(37, 1);
+	spmp8000_pinmux_pgs_set(38, 1);
+
+	/* Set up SAR-GPIOs for the ADC power supply */
+	writel(0xCC, REG_SCU_A(SCU_A_SAR_GPIO_CTRL));
+	writel(0x3, REG_SCU_A(SCU_A_SAR_GPIO_OEN));
+	writel(0xC, REG_SCU_A(SCU_A_SAR_GPIO_O));
+
+	/* Switch UART_MISC_TX and RX to GPIO3 pins */
+	spmp8000_pinmux_pgs_set(43, 2);
+	spmp8000_pinmux_pgs_set(44, 2);
+
+	/* Codec errata fix */
+	writel(SCU_A_CODEC_CFG_VAL, REG_SCU_A(SCU_A_CODEC_CFG));
+
+	/* DT and platform device setup */
+
+	/* Create platform devces */
+	of_platform_populate(NULL, of_default_bus_match_table,
+			     letcool_auxdata_lookup, NULL);
+
+	/* Fix-up GPIO3 platform device resources to make gpio-generic work.
+	 * Should be removed once it has DT bindings. */
+	gpio3_np = of_find_compatible_node(NULL, NULL, "basic-mmio-gpio");
+	if (!gpio3_np) {
+		pr_err("letcool: Can't find gpio3 DT node\n");
+		return;
+	}
+
+	gpio3_pdev = of_find_device_by_node(gpio3_np);
+	if (!gpio3_pdev) {
+		pr_err("letcool: Can't find gpio3 platform device\n");
+		return;
+	}
+
+	gpio3_pdev->num_resources = ARRAY_SIZE(spmp8000_gpio3_resources);
+	gpio3_pdev->resource = spmp8000_gpio3_resources;
+}
+
+static const char *letcool_dt_match[] __initconst = {
+	"gameware,letcool",
+	NULL,
+};
+
+DT_MACHINE_START(LETCOOL, "Letcool N350JP")
+	.map_io		= spmp8000_map_io,
+	.init_early	= spmp8000_init_clkdev,
+	.init_irq	= spmp8000_init_irq,
+	.handle_irq	= vic_handle_irq,
+	.nr_irqs	= SPMP8000_TOTAL_IRQS,
+	.init_machine	= letcool_dt_init,
+	.timer		= &spmp8000_sys_timer,
+	.dt_compat	= letcool_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-spmp8000/include/mach/spmp8000fb.h b/arch/arm/mach-spmp8000/include/mach/spmp8000fb.h
new file mode 100644
index 0000000..525e23f
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/spmp8000fb.h
@@ -0,0 +1,32 @@
+/*
+ * SPMP8000 machines LCD controller support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MACH_SPMP8000_FB_H__
+#define __MACH_SPMP8000_FB_H__
+
+#include <linux/fb.h>
+
+#define TFT_FMT_RGB565		0
+#define TFT_FMT_SRGB		1
+#define TFT_FMT_RGBDM		2
+#define TFT_FMT_CCIR601		3
+#define TFT_FMT_CCIR656		4
+#define TFT_FMT_RGB24B		5
+#define TFT_FMT_CCIR_720M	(1 << 8)
+
+struct spmp8000fb_pdata {
+	u32			data_fmt;
+	u32			xres_virtual;
+	u32			yres_virtual;
+	u32			bpp;
+	struct fb_videomode	mode;
+};
+
+#endif /* __MACH_SPMP8000_FB_H__ */
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 9/9] ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine
From: Zoltan Devai @ 2011-10-09 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/Kconfig  |   16 ++++++++++++++++
 arch/arm/Makefile |    1 +
 2 files changed, 17 insertions(+), 0 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9426e26..f34f10d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -831,6 +831,22 @@ config ARCH_SHARK
 	  Support for the StrongARM based Digital DNARD machine, also known
 	  as "Shark" (<http://www.shark-linux.de/shark.html>).
 
+config ARCH_SPMP8000
+	bool "Sunplus SPMP8000"
+	select CPU_ARM926T
+	select ARM_VIC
+	select GENERIC_IRQ_CHIP
+	select MULTI_IRQ_HANDLER
+	select CLKSRC_MMIO
+	select GENERIC_CLOCKEVENTS
+	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select USE_OF
+	select CLKDEV_LOOKUP
+	select HAVE_PWM
+	select ARCH_HAS_CPUFREQ
+	help
+	  Support for Sunplus SPMP8000A/8010A/8016A SoC.
+
 config ARCH_TCC_926
 	bool "Telechips TCC ARM926-based systems"
 	select CLKSRC_MMIO
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index e0ac6eb..f837a3b 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -180,6 +180,7 @@ machine-$(CONFIG_ARCH_EXYNOS4)		:= exynos4
 machine-$(CONFIG_ARCH_SA1100)		:= sa1100
 machine-$(CONFIG_ARCH_SHARK)		:= shark
 machine-$(CONFIG_ARCH_SHMOBILE) 	:= shmobile
+machine-$(CONFIG_ARCH_SPMP8000)		:= spmp8000
 machine-$(CONFIG_ARCH_TCC8K)		:= tcc8k
 machine-$(CONFIG_ARCH_TEGRA)		:= tegra
 machine-$(CONFIG_ARCH_U300)		:= u300
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 2/9] ARM: SPMP8000: Add machine base files
From: Jamie Iles @ 2011-10-09 17:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-3-git-send-email-zoss@devai.org>

Hi Zoltan,

A couple of minor comments inline but this looks really good!

Jamie

On Sun, Oct 09, 2011 at 06:36:05PM +0200, Zoltan Devai wrote:
[...]
> diff --git a/arch/arm/mach-spmp8000/Makefile b/arch/arm/mach-spmp8000/Makefile
> new file mode 100644
> index 0000000..9d35cca
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/Makefile
> @@ -0,0 +1,11 @@
> +#
> +# Makefile for the linux kernel.
> +#

Nit: I don't think this comment adds a great deal of value.

> +obj-y 			:= core.o
> +obj-y			+= timer.o
> +obj-y			+= pinmux.o
> +obj-y			+= clock.o
> +obj-y			+= clkdev.o
> +obj-y			+= board_letcool.o
> +obj-y			+= adc.o
> +obj-y			+= pwm.o
> diff --git a/arch/arm/mach-spmp8000/core.c 
> b/arch/arm/mach-spmp8000/core.c
> new file mode 100644
> index 0000000..ba05614
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/core.c
> @@ -0,0 +1,103 @@
> +/*
> + * SPMP8000 machines core functions
> + *
> + * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <asm/mach/map.h>
> +#include <asm/hardware/vic.h>
> +#include <mach/hardware.h>
> +#include <mach/scu.h>
> +
> +/* Static mappings:
> + * SCU: timer needs clk support which is inited in init_early
> + * UART: needed for earlyprintk
> + */
> +static struct map_desc io_desc[] __initdata = {
> +	{
> +		.virtual	= IO_ADDRESS(SPMP8000_SCU_A_BASE),
> +		.pfn		= __phys_to_pfn(SPMP8000_SCU_A_BASE),
> +		.length		= SPMP8000_SCU_A_SIZE,
> +		.type		= MT_DEVICE,
> +	}, {
> +		.virtual	= IO_ADDRESS(SPMP8000_SCU_B_BASE),
> +		.pfn		= __phys_to_pfn(SPMP8000_SCU_B_BASE),
> +		.length		= SPMP8000_SCU_B_SIZE,
> +		.type		= MT_DEVICE,
> +	}, {
> +		.virtual	= IO_ADDRESS(SPMP8000_SCU_C_BASE),
> +		.pfn		= __phys_to_pfn(SPMP8000_SCU_C_BASE),
> +		.length		= SPMP8000_SCU_C_SIZE,
> +		.type		= MT_DEVICE,
> +	},
> +#ifdef CONFIG_DEBUG_LL
> +	{
> +		.virtual	= IO_ADDRESS(SPMP8000_UARTC0_BASE),
> +		.pfn		= __phys_to_pfn(SPMP8000_UARTC0_BASE),
> +		.length		= SPMP8000_UARTC0_SIZE,
> +		.type		= MT_DEVICE,
> +	},
> +#endif
> +};
> +
> +#define IO_PTR(x)	((void __iomem *)IO_ADDRESS(x))

Generally IO_ADDRESS returns a void __iomem pointer so it would be 
easier if you could have the cast in there (though it may need to be 
enclosed in #ifndef __ASSEMBLY__ conditionals).

> +/* Make the lookup of SCU base and clk enable registers easy for the clk
> + * code, as they are not at the same offset in each controller */
> +struct scu_reg_t scu_regs[3] = {
> +	{	/* SCU_A */
> +		.base = IO_PTR(SPMP8000_SCU_A_BASE),
> +		.clken = IO_PTR(SPMP8000_SCU_A_BASE) + SCU_A_PERI_CLKEN,
> +	},
> +	{	/* SCU_B */
> +		.base = IO_PTR(SPMP8000_SCU_B_BASE),
> +		.clken = IO_PTR(SPMP8000_SCU_B_BASE) + SCU_B_PERI_CLKEN,
> +	},
> +	{	/* SCU_C */
> +		.base = IO_PTR(SPMP8000_SCU_C_BASE),
> +		.clken = IO_PTR(SPMP8000_SCU_C_BASE) + SCU_C_PERI_CLKEN,
> +	},
> +};
> +
> +/* DMA controller names to be used for the filter
> + * function of the DMA-Engine API. */
> +char *spmp8000_dma_controller_names[] = {

const char * const?

> +	"93010000.dma",		/* APBDMA_A */
> +	"92b00000.dma",		/* APBDMA_C */
> +};
> +
> +void __init spmp8000_map_io(void)
> +{
> +	iotable_init(io_desc, ARRAY_SIZE(io_desc));
> +}
> +
> +void __init spmp8000_init_irq(void)
> +{
> +	struct device_node *np;
> +	int ret;
> +
> +	np = of_find_compatible_node(NULL, NULL, "arm,pl192");
> +	if (!np)
> +		panic("Can't find VIC0 interrupt controller\n");
> +
> +	ret = vic_of_init(np, NULL);
> +	if (ret)
> +		panic("Can't init VIC0 interrupt controller\n");
> +
> +	np = of_find_compatible_node(np, NULL, "arm,pl192");
> +	if (!np)
> +		panic("Can't find VIC1 interrupt controller\n");
> +
> +	ret = vic_of_init(np, NULL);
> +	if (ret)
> +		panic("Can't init VIC1 interrupt controller\n");
> +
> +	of_node_put(np);

So with Rob Herring's of_irq_init patches you should be able to reduce 
this to:

	static const struct of_device_id spmp8000_vic_matches[] = {
		{ .compatible = "arm,pl192", .data = vic_of_init },
	};
	
	of_irq_init(spmp8000_vic_matches);

and that will handle both VIC's and ordering.

> +}
> diff --git a/arch/arm/mach-spmp8000/include/mach/core.h b/arch/arm/mach-spmp8000/include/mach/core.h
> new file mode 100644
> index 0000000..fa1d522
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/include/mach/core.h
> @@ -0,0 +1,29 @@
> +/*
> + * SPMP8000 generic includes
> + *
> + * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#ifndef __MACH_SPMP8000_GENERIC_H__
> +#define __MACH_SPMP8000_GENERIC_H__
> +
> +/* core.c */
> +extern void __init spmp8000_map_io(void);
> +extern void __init spmp8000_init_irq(void);

Nit: you don't need the __init attributes in the header files.

> +extern struct sys_timer spmp8000_sys_timer;
> +
> +/* clkdev.c */
> +extern void __init spmp8000_init_clkdev(void);
> +extern void spmp8000_update_arm_freqs(void); /* For cpufreq driver */
> +
> +/* pinmux.c */
> +extern void spmp8000_pinmux_pgc_set(int pc, unsigned int conf);
> +extern unsigned int spmp8000_pinmux_pgc_get(int pc);
> +extern void spmp8000_pinmux_pgs_set(unsigned int pg, unsigned int func);
> +extern int spmp8000_pinmux_pgs_get(int pg);
> +
> +#endif /* __MACH_SPMP8000_GENERIC_H__ */
[...]
> diff --git a/arch/arm/mach-spmp8000/include/mach/dma.h b/arch/arm/mach-spmp8000/include/mach/dma.h
> new file mode 100644
> index 0000000..44bc9f2
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/include/mach/dma.h
> @@ -0,0 +1,45 @@
> +/*
> + * SPMP8000 dma.h
> + *
> + * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +#include <linux/module.h>
> +#include <linux/dmaengine.h>

It may be worth splitting the DMA part out into a separate patch unless 
this really is required for basic functionality.

> +
> +#ifndef __MACH_SPMP8000_DMA_H__
> +#define __MACH_SPMP8000_DMA_H__
> +
> +enum spmp8000_dma_controller {
> +	SPMP8000_APBDMA_A	= 0,
> +	SPMP8000_APBDMA_C,
> +};
> +
> +extern char *spmp8000_dma_controller_names[];
> +
> +struct spmp8000_dma_params {
> +	char				*dma_controller;
> +	dma_addr_t			dma_address;
> +	enum dma_slave_buswidth		dma_width;
> +	int				maxburst;
> +};

dmaengine has dma_slave_config that could be used for this, but I don't 
see any of this file being used in this series so perhaps it's worth 
adding in after the core stuff has been merged?

> +
> +/* Driver parameters */
> +#define SPMP8000_APBDMA_MAX_PERIODS	64
> +
> +static bool spmp8000_dma_filter(struct dma_chan *chan, const char *name)
> +{
> +	int ret;
> +
> +	ret = strcmp(dev_name(chan->device->dev), name);
> +
> +	if (ret)
> +		return false;
> +
> +	return true;

I think you can reduce this function to just

	return !strcmp(dev_name(chan->device->dev), name);

and the compiler will do the right thing with int->bool conversion.

> diff --git a/arch/arm/mach-spmp8000/include/mach/gpio.h 
> b/arch/arm/mach-spmp8000/include/mach/gpio.h
> new file mode 100644
> index 0000000..3eafac2
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/include/mach/gpio.h
> @@ -0,0 +1,21 @@
> +/*
> + * SPMP8000 machines gpio support
> + *
> + * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#ifndef __MACH_SPMP8000_GPIO_H__
> +#define __MACH_SPMP8000_GPIO_H__
> +
> +#include <asm-generic/gpio.h>
> +
> +#define gpio_get_value		__gpio_get_value
> +#define gpio_set_value		__gpio_set_value
> +#define gpio_cansleep		__gpio_cansleep
> +#define gpio_to_irq		__gpio_to_irq

Russell has a patch (ARM: gpio: make trivial GPIOLIB implementation the 
default) in for-next that means you shouldn't need these definitions 
anymore.

> diff --git a/arch/arm/mach-spmp8000/include/mach/irqs.h 
> b/arch/arm/mach-spmp8000/include/mach/irqs.h
> new file mode 100644
> index 0000000..7f305d3
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/include/mach/irqs.h
> @@ -0,0 +1,21 @@
> +/*
> + * SPMP8000 irqs.h
> + *
> + * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#ifndef __MACH_SPMP8000_IRQS_H__
> +#define __MACH_SPMP8000_IRQS_H__
> +
> +#define SPMP8000_HW_IRQS		64
> +#define SPMP8000_GPIO_IRQS_START	SPMP8000_HW_IRQS
> +#define SPMP8000_GPIO_IRQS		(16 + 32)
> +#define SPMP8000_TOTAL_IRQS		(SPMP8000_HW_IRQS + SPMP8000_GPIO_IRQS)
> +
> +#define NR_IRQS				SPMP8000_TOTAL_IRQS

I think that if you use my VIC patches then you can actually define 
NR_IRQS to 0 as all IRQ descs are dynamically allocated (otherwise 
you'll be reserving a bunch of irq_descs that never get used).

> diff --git a/arch/arm/mach-spmp8000/include/mach/regs-timer.h 
> b/arch/arm/mach-spmp8000/include/mach/regs-timer.h
> new file mode 100644
> index 0000000..90735df
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/include/mach/regs-timer.h
> @@ -0,0 +1,32 @@
> +/*
> + * SPMP8000 timer support
> + *
> + * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + *
> + * These timer reg definitions are used by the timer and pwm drivers
> + */

If these definitions are only being used inside arch/arm/mach-spmp8000 
then I'd put the headers in there rather than the 
arch/arm/mach-spmp8000/include/mach subdir so that the visibility is 
restricted to the mach code.

> +#ifndef __MACH_SPMP8000_REGS_TIMER_H__
> +#define __MACH_SPMP8000_REGS_TIMER_H__
> +
> +#define SPMP8000_TMRB(tnum)	(tnum * 0x20)
> +#define SPMP8000_TMRB_CTR	0x00
> +#define SPMP8000_TMRB_CTR_TE	BIT(0)
> +#define SPMP8000_TMRB_CTR_IE	BIT(1)
> +#define SPMP8000_TMRB_CTR_OE	BIT(2)
> +#define SPMP8000_TMRB_CTR_PWMON	BIT(3)
> +#define SPMP8000_TMRB_CTR_UD	BIT(4)
> +#define SPMP8000_TMRB_CTR_UDS	BIT(5)
> +#define SPMP8000_TMRB_CTR_OM	BIT(6)
> +#define SPMP8000_TMRB_CTR_ES_SH	8
> +#define SPMP8000_TMRB_CTR_M_SH	10
> +#define SPMP8000_TMRB_PSR	0x04
> +#define SPMP8000_TMRB_LDR	0x08
> +#define SPMP8000_TMRB_VLR	0x08
> +#define SPMP8000_TMRB_ISR	0x0C
> +#define SPMP8000_TMRB_CMP	0x10
> +
> +#endif /* __MACH_SPMP8000_REGS_TIMER_H__ */
> diff --git a/arch/arm/mach-spmp8000/include/mach/scu.h b/arch/arm/mach-spmp8000/include/mach/scu.h
> new file mode 100644
> index 0000000..e3a98d4
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/include/mach/scu.h

Same here as the timer regs stuff.

> @@ -0,0 +1,146 @@
> +/*
> + * SPMP8000 System Control Unit register definitions
> + * SCUs are a random collection of reset, clock, gpio, pinmux, etc. controllers
> + * so that these definitions are needed at several places.
> + *
> + * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/bitops.h>
> +#include <mach/hardware.h>
> +
> +#ifndef __MACH_SPMP8000_SCU_H__
> +#define __MACH_SPMP8000_SCU_H__
> +
> +/* Used by to the clock code to directly access the SCU base and clock
> + * enabling registers, without looking up the according registers first.
> + */
> +struct scu_reg_t {

Generally speaking _t isn't used for structs.

> +	void *base;
> +	void *clken;

Shouldn't these be void __iomem *?

> +};
> +
> +extern struct scu_reg_t scu_regs[];
> +
> +#define REG_SCU_BASE(x)		scu_regs[x].base
> +#define REG_SCU_CLKEN(x)	scu_regs[x].clken
> +#define REG_SCU_A_ID		0
> +#define REG_SCU_B_ID		1
> +#define REG_SCU_C_ID		2
> +#define REG_SCU_A(x)		(scu_regs[REG_SCU_A_ID].base + x)
> +#define REG_SCU_B(x)		(scu_regs[REG_SCU_B_ID].base + x)
> +#define REG_SCU_C(x)		(scu_regs[REG_SCU_C_ID].base + x)

macros that assume there is a variable in scope with a given name 
(scu_regs) are generally frowned upon as it's difficult to see what's 
going on and can be a little error prone.

> diff --git a/arch/arm/mach-spmp8000/include/mach/system.h 
> b/arch/arm/mach-spmp8000/include/mach/system.h
> new file mode 100644
> index 0000000..be53ff3
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/include/mach/system.h
> @@ -0,0 +1,45 @@
> +/*
> + * SPMP8000 system.h
> + *
> + * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#ifndef __MACH_SPMP8000_SYSTEM_H__
> +#define __MACH_SPMP8000_SYSTEM_H__
> +
> +#include <linux/kernel.h>
> +#include <linux/io.h>
> +
> +#define SPMP8000_WDT_BASE	0x90001000
> +#define SPMP8000_WDT_SIZE	0x1000
> +
> +#define SPMP8000_WDT_CTR	0x00
> +#define SPMP8000_WDT_CTR_TE	BIT(0)
> +#define SPMP8000_WDT_CTR_RE	BIT(3)
> +
> +static inline void arch_idle(void)
> +{
> +	cpu_do_idle();
> +}
> +
> +static inline void arch_reset(char mode, const char *cmd)
> +{
> +	void *base;
> +
> +	base = ioremap(SPMP8000_WDT_BASE, SPMP8000_WDT_SIZE);
> +	if (!base) {
> +		pr_err("spmp8000: Can't ioremap watchdog regs for reset. "
> +			"Halt.");
> +		while (1);
> +	}

It may be worth doing the ioremap earlier when the system is in a known 
good state with all functions available rather than at reset time.

> +
> +	/* Trigger a watchdog reset */
> +	writel(SPMP8000_WDT_CTR_RE | SPMP8000_WDT_CTR_TE,
> +			base + SPMP8000_WDT_CTR);
> +}
[...]
> diff --git a/arch/arm/mach-spmp8000/timer.c 
> b/arch/arm/mach-spmp8000/timer.c
> new file mode 100644
> index 0000000..7d5d0eb
> --- /dev/null
> +++ b/arch/arm/mach-spmp8000/timer.c
> @@ -0,0 +1,160 @@
> +/*
> + * SPMP8000 machines timer functions
> + *
> + * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/irq.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/bitops.h>
> +#include <linux/interrupt.h>
> +#include <linux/clockchips.h>
> +#include <linux/clk.h>
> +#include <asm/mach/time.h>
> +
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +
> +#include <mach/hardware.h>
> +#include <mach/regs-timer.h>
> +#include <mach/irqs.h>
> +
> +static unsigned long clkrate;
> +static const unsigned int tickrate = 1012500;
> +
> +/* The TIMER_B block is used as system timer
> + * T2: Clocksource
> + * T1: Clockevent
> + * T0: PWM for LCD backlight
> + * T3,4: Could be used as additional clockevent devices
> + * Timer constraints:
> + * - WDT: Can't clear the irq directly, only by resetting the whole counter
> + *   in the ISR, which means that IRQs will jitter
> + * - Timer_B: Can't reset the timer value, so at start, the first IRQ
> + *   will happen at some random time.
> + */
> +#define		CS_TIMER	2
> +#define		CE_TIMER	1

Nit: removing the extra spaces between the define and the CS_TIMER...  
might make it easier to grep for.

> +static void __iomem *cs_base;
> +static void __iomem *ce_base;
> +
> +static void tmrb_set_mode(enum clock_event_mode mode,
> +				struct clock_event_device *dev)
> +{
> +	switch (mode) {
> +	case CLOCK_EVT_MODE_PERIODIC:
> +		writel((tickrate / HZ), ce_base + SPMP8000_TMRB_LDR);
> +		/* Configure as periodic, down counter, IEN, enable timer */
> +		writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_IE |
> +				(1 << SPMP8000_TMRB_CTR_M_SH),
> +				ce_base + SPMP8000_TMRB_CTR);
> +		break;
> +	case CLOCK_EVT_MODE_ONESHOT:
> +		BUG();
> +		break;

This case can be folded into the CLOCK_EVT_MODE_RESUME case (and changed 
to be a default case).

> +	case CLOCK_EVT_MODE_SHUTDOWN:
> +	case CLOCK_EVT_MODE_UNUSED:
> +		/* Disable timer */
> +		writel(0, ce_base + SPMP8000_TMRB_CTR);
> +		break;
> +	case CLOCK_EVT_MODE_RESUME:
> +		BUG();
> +		break;
> +	}
> +}
> +
> +static struct clock_event_device tmrb1_clkevt = {
> +	.name		= "tmrb1",
> +	.features	= CLOCK_EVT_FEAT_PERIODIC,
> +	.rating		= 200,
> +	.set_mode	= tmrb_set_mode,
> +};
> +
> +static irqreturn_t tmrb1_isr(int irq, void *dev_id)
> +{
> +	tmrb1_clkevt.event_handler(&tmrb1_clkevt);
> +
> +	/* Clear IRQ */
> +	writel(0, ce_base + SPMP8000_TMRB_ISR);
> +
> +	return IRQ_HANDLED;
> +};
> +
> +static struct irqaction tmrb1_irq = {
> +	.name		= "tmrb1_irq",
> +	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
> +	.handler	= tmrb1_isr,
> +};
> +
> +static void __init spmp8000_sys_timer_init(void)
> +{
> +	struct device_node *np;
> +	unsigned int irq;
> +	struct clk *clk;
> +	void *tmrb_base;
> +
> +	np = of_find_compatible_node(NULL, NULL, "sunplus,spmp8000-timer");
> +	if (!np)
> +		panic("spmp8000: unable to find timer node in dtb\n");
> +
> +	tmrb_base = of_iomap(np, 0);
> +	if (!tmrb_base)
> +		panic("spmp8000: unable to map timer cpu registers\n");
> +
> +	irq = of_irq_to_resource(np, CE_TIMER, NULL);
> +	if (irq == NO_IRQ)
> +		panic("spmp8000: unable to get interrupts of timer\n");
> +
> +	of_node_put(np);
> +
> +	cs_base = tmrb_base + SPMP8000_TMRB(CS_TIMER);
> +	ce_base = tmrb_base + SPMP8000_TMRB(CE_TIMER);
> +
> +	clk = clk_get(NULL, "arm_apb");
> +	if (IS_ERR_OR_NULL(clk))

NULL can actually be a valid clk cookie so this should just be 
IS_ERR(clk).

> +		panic("spmp8000: Can't get clock for timer device");
> +
> +	clk_enable(clk);

Should probably check for success/failure here.

> +	clkrate = clk_get_rate(clk);
> +
> +	/* Clocksource */
> +	/* Disable timer */
> +	writel(0, cs_base + SPMP8000_TMRB_CTR);
> +
> +	/* Reset counter value
> +	 * Not really possible unless setting end-1 LDR value and waiting
> +	 * until the counter reaches that */
> +
> +	/* Prescale timer */
> +	writel((clkrate / tickrate) - 1, cs_base + SPMP8000_TMRB_PSR);
> +
> +	/* Register the clocksource */
> +	clocksource_mmio_init(cs_base + SPMP8000_TMRB_VLR, "tmrb2",
> +				tickrate, 200, 16, clocksource_mmio_readl_up);
> +
> +	/* Configure as free running (0 - 0xFFFF), up counter, enable timer */
> +	writel(SPMP8000_TMRB_CTR_TE | SPMP8000_TMRB_CTR_UD,
> +		cs_base + SPMP8000_TMRB_CTR);
> +
> +	/* Clockevent */
> +	setup_irq(irq, &tmrb1_irq);
> +
> +	/* Disable timer */
> +	writel(0, ce_base + SPMP8000_TMRB_CTR);
> +
> +	/* Prescale timer */
> +	writel((clkrate / tickrate) - 1, ce_base + SPMP8000_TMRB_PSR);
> +
> +	clockevents_register_device(&tmrb1_clkevt);
> +}
> +
> +struct sys_timer spmp8000_sys_timer = {
> +	.init		= spmp8000_sys_timer_init,
> +};

^ permalink raw reply

* [PATCH 9/9] ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine
From: Jamie Iles @ 2011-10-09 17:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1318178172-7965-10-git-send-email-zoss@devai.org>

Hi Zoltan,

On Sun, Oct 09, 2011 at 06:36:12PM +0200, Zoltan Devai wrote:
> Signed-off-by: Zoltan Devai <zoss@devai.org>
> ---
>  arch/arm/Kconfig  |   16 ++++++++++++++++
>  arch/arm/Makefile |    1 +
>  2 files changed, 17 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 9426e26..f34f10d 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -831,6 +831,22 @@ config ARCH_SHARK
>  	  Support for the StrongARM based Digital DNARD machine, also known
>  	  as "Shark" (<http://www.shark-linux.de/shark.html>).
>  
> +config ARCH_SPMP8000
> +	bool "Sunplus SPMP8000"
> +	select CPU_ARM926T
> +	select ARM_VIC
> +	select GENERIC_IRQ_CHIP
> +	select MULTI_IRQ_HANDLER
> +	select CLKSRC_MMIO
> +	select GENERIC_CLOCKEVENTS
> +	select ARCH_WANT_OPTIONAL_GPIOLIB
> +	select USE_OF
> +	select CLKDEV_LOOKUP
> +	select HAVE_PWM
> +	select ARCH_HAS_CPUFREQ

I can't see anything using cpufreq yet so perhaps this can be removed 
for now?

Jamie

^ permalink raw reply

* [PATCH 1/5] drivercore: add new error value for deferred probe
From: Grant Likely @ 2011-10-09 22:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20111008001237.GA30551@kroah.com>

On Fri, Oct 7, 2011 at 6:12 PM, Greg KH <greg@kroah.com> wrote:
> On Fri, Oct 07, 2011 at 07:28:33PM -0400, Valdis.Kletnieks at vt.edu wrote:
>> On Fri, 07 Oct 2011 16:12:45 MDT, Grant Likely said:
>> > On Fri, Oct 7, 2011 at 12:43 AM, Greg KH <greg@kroah.com> wrote:
>> > > On Fri, Oct 07, 2011 at 10:33:06AM +0500, G, Manjunath Kondaiah wrote:
>>
>> > >> +#define EPROBE_DEFER 517 ? ? /* restart probe again after some time */
>> > >
>> > > Can we really do this?
>>
>> > According to Arnd, yes this is okay.
>>
>> > > ?Isn't this some user/kernel api here?
>>
>> > > What's wrong with just "overloading" on top of an existing error code?
>> > > Surely one of the other 516 types could be used here, right?
>>
>> > overloading makes it really hard to find the users at a later date.
>>
>> Would proposing '#define EPROBE_DEFER EAGAIN' be acceptable to everybody? That
>> would allow overloading EAGAIN, but still make it easy to tell the usages apart
>> if we need to separate them later...
>
> Yes, please do that, it is what USB does for it's internal error code
> handling.

Really?  When we've only currently used approximately 2^9 of a 2^31
numberspace?  I'm fine with making sure that the number doesn't show
up in the userspace headers, but it makes no sense to overload the
#defines.  Particularly so in this case where it isn't feasible to
audit every driver to figure out what probe might possibly return.  It
is well within the realm of possibility that existing drivers are
already returning -EAGAIN.

Besides; linux/errno.h *already* has linux-internal error codes that
do not get exported out to userspace.  There is an #ifdef __KERNEL__
block around ERESTARTSYS through EIOCBRETRY which is filtered out when
exporting headers.  I can't see any possible reason why we wouldn't
add Linux internal error codes here.

g.



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* [PATCH] arm: mm: Fix infinite looping issue
From: Hyok S. Choi @ 2011-10-10  0:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAKrE-KfYAwsPGgFQyV6M3tYpzWPgZCGdRGNxQUiB4ESC9i_L8A@mail.gmail.com>

Seems to be okay.

Thanks,
Hyok

-----Original Message-----
From: Girish KS [mailto:girishks2000 at gmail.com] 
Sent: Saturday, October 08, 2011 2:25 PM
To: Girish K S
Cc: linux-arm-kernel at lists.infradead.org; rmk+kernel at arm.linux.org.uk;
linux-samsung-soc at vger.kernel.org; hyok.choi at samsung.com; patches at linaro.org
Subject: Re: [PATCH] arm: mm: Fix infinite looping issue

On Tue, Oct 4, 2011 at 4:08 PM, Girish K S <girish.shivananjappa@linaro.org>
wrote:
> This patch fixes the problem of infinite looping while booting.
>
> The bne instruction expects the z flag to be set to break the loop.
> The (mov r1, r1, lsr #1) doesn't behave in the expected way. If 
> replaced with the movs instruction then the execution will exit the 
> loop.
>
> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
> ---
> ?arch/arm/mm/proc-arm740.S | ? ?4 ++--
> ?arch/arm/mm/proc-arm940.S | ? ?4 ++--
> ?arch/arm/mm/proc-arm946.S | ? ?4 ++--
> ?3 files changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm/mm/proc-arm740.S b/arch/arm/mm/proc-arm740.S 
> index 4506be3..9284f10 100644
> --- a/arch/arm/mm/proc-arm740.S
> +++ b/arch/arm/mm/proc-arm740.S
> @@ -77,7 +77,7 @@ __arm740_setup:
> ? ? ? ?ldr ? ? r1, =(CONFIG_DRAM_SIZE >> 12) ? @ size of RAM (must be 
> >= 4KB)
> ? ? ? ?mov ? ? r2, #10 ? ? ? ? ? ? ? ? ? ? ? ? @ 11 is the minimum 
> (4KB)
> ?1: ? ? add ? ? r2, r2, #1 ? ? ? ? ? ? ? ? ? ? ?@ area size *= 2
> - ? ? ? mov ? ? r1, r1, lsr #1
> + ? ? ? movs ? ?r1, r1, lsr #1 ? ? ? ? ? ? ? ? ?@ set zero bit if r1=0
> ? ? ? ?bne ? ? 1b ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@ count not zero 
> r-shift
> ? ? ? ?orr ? ? r0, r0, r2, lsl #1 ? ? ? ? ? ? ?@ the area register 
> value
> ? ? ? ?orr ? ? r0, r0, #1 ? ? ? ? ? ? ? ? ? ? ?@ set enable bit @@ 
> -87,7 +87,7 @@ __arm740_setup:
> ? ? ? ?ldr ? ? r1, =(CONFIG_FLASH_SIZE >> 12) ?@ size of FLASH (must 
> be >= 4KB)
> ? ? ? ?mov ? ? r2, #10 ? ? ? ? ? ? ? ? ? ? ? ? @ 11 is the minimum 
> (4KB)
> ?1: ? ? add ? ? r2, r2, #1 ? ? ? ? ? ? ? ? ? ? ?@ area size *= 2
> - ? ? ? mov ? ? r1, r1, lsr #1
> + ? ? ? movs ? ?r1, r1, lsr #1 ? ? ? ? ? ? ? ? ?@ set zero bit if r1=0
> ? ? ? ?bne ? ? 1b ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@ count not zero 
> r-shift
> ? ? ? ?orr ? ? r0, r0, r2, lsl #1 ? ? ? ? ? ? ?@ the area register 
> value
> ? ? ? ?orr ? ? r0, r0, #1 ? ? ? ? ? ? ? ? ? ? ?@ set enable bit diff 
> --git a/arch/arm/mm/proc-arm940.S b/arch/arm/mm/proc-arm940.S index 
> ac750d5..e639034 100644
> --- a/arch/arm/mm/proc-arm940.S
> +++ b/arch/arm/mm/proc-arm940.S
> @@ -296,7 +296,7 @@ __arm940_setup:
> ? ? ? ?ldr ? ? r1, =(CONFIG_DRAM_SIZE >> 12) ? @ size of RAM (must be 
> >= 4KB)
> ? ? ? ?mov ? ? r2, #10 ? ? ? ? ? ? ? ? ? ? ? ? @ 11 is the minimum 
> (4KB)
> ?1: ? ? add ? ? r2, r2, #1 ? ? ? ? ? ? ? ? ? ? ?@ area size *= 2
> - ? ? ? mov ? ? r1, r1, lsr #1
> + ? ? ? movs ? ?r1, r1, lsr #1 ? ? ? ? ? ? ? ? ?@ set zero bit if r1=0
> ? ? ? ?bne ? ? 1b ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@ count not zero 
> r-shift
> ? ? ? ?orr ? ? r0, r0, r2, lsl #1 ? ? ? ? ? ? ?@ the area register 
> value
> ? ? ? ?orr ? ? r0, r0, #1 ? ? ? ? ? ? ? ? ? ? ?@ set enable bit @@ 
> -307,7 +307,7 @@ __arm940_setup:
> ? ? ? ?ldr ? ? r1, =(CONFIG_FLASH_SIZE >> 12) ?@ size of FLASH (must 
> be >= 4KB)
> ? ? ? ?mov ? ? r2, #10 ? ? ? ? ? ? ? ? ? ? ? ? @ 11 is the minimum 
> (4KB)
> ?1: ? ? add ? ? r2, r2, #1 ? ? ? ? ? ? ? ? ? ? ?@ area size *= 2
> - ? ? ? mov ? ? r1, r1, lsr #1
> + ? ? ? movs ? ?r1, r1, lsr #1 ? ? ? ? ? ? ? ? ?@ set zero bit if r1=0
> ? ? ? ?bne ? ? 1b ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@ count not zero 
> r-shift
> ? ? ? ?orr ? ? r0, r0, r2, lsl #1 ? ? ? ? ? ? ?@ the area register 
> value
> ? ? ? ?orr ? ? r0, r0, #1 ? ? ? ? ? ? ? ? ? ? ?@ set enable bit diff 
> --git a/arch/arm/mm/proc-arm946.S b/arch/arm/mm/proc-arm946.S index 
> 683af3a..731388a 100644
> --- a/arch/arm/mm/proc-arm946.S
> +++ b/arch/arm/mm/proc-arm946.S
> @@ -341,7 +341,7 @@ __arm946_setup:
> ? ? ? ?ldr ? ? r1, =(CONFIG_DRAM_SIZE >> 12) ? @ size of RAM (must be 
> >= 4KB)
> ? ? ? ?mov ? ? r2, #10 ? ? ? ? ? ? ? ? ? ? ? ? @ 11 is the minimum 
> (4KB)
> ?1: ? ? add ? ? r2, r2, #1 ? ? ? ? ? ? ? ? ? ? ?@ area size *= 2
> - ? ? ? mov ? ? r1, r1, lsr #1
> + ? ? ? movs ? ?r1, r1, lsr #1 ? ? ? ? ? ? ? ? ?@ set zero bit if r1=0
> ? ? ? ?bne ? ? 1b ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@ count not zero 
> r-shift
> ? ? ? ?orr ? ? r0, r0, r2, lsl #1 ? ? ? ? ? ? ?@ the region register 
> value
> ? ? ? ?orr ? ? r0, r0, #1 ? ? ? ? ? ? ? ? ? ? ?@ set enable bit @@ 
> -351,7 +351,7 @@ __arm946_setup:
> ? ? ? ?ldr ? ? r1, =(CONFIG_FLASH_SIZE >> 12) ?@ size of FLASH (must 
> be >= 4KB)
> ? ? ? ?mov ? ? r2, #10 ? ? ? ? ? ? ? ? ? ? ? ? @ 11 is the minimum 
> (4KB)
> ?1: ? ? add ? ? r2, r2, #1 ? ? ? ? ? ? ? ? ? ? ?@ area size *= 2
> - ? ? ? mov ? ? r1, r1, lsr #1
> + ? ? ? movs ? ?r1, r1, lsr #1 ? ? ? ? ? ? ? ? ?@ set zero bit if r1=0
> ? ? ? ?bne ? ? 1b ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@ count not zero 
> r-shift
> ? ? ? ?orr ? ? r0, r0, r2, lsl #1 ? ? ? ? ? ? ?@ the region register 
> value
> ? ? ? ?orr ? ? r0, r0, #1 ? ? ? ? ? ? ? ? ? ? ?@ set enable bit
> --
> 1.7.1
>
>Hello Mr choi,
 Sorry for my previous top posting.
If there is no issue with this patch can you apply to your for-next tree

> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

^ permalink raw reply

* [PATCH 1/5] drivercore: add new error value for deferred probe
From: Greg KH @ 2011-10-10  1:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACxGe6t+PfnNGniYUj3pbK22uqjqkbh2+D=7qvvd_-Yd4yR0dw@mail.gmail.com>

On Sun, Oct 09, 2011 at 04:59:31PM -0600, Grant Likely wrote:
> On Fri, Oct 7, 2011 at 6:12 PM, Greg KH <greg@kroah.com> wrote:
> > On Fri, Oct 07, 2011 at 07:28:33PM -0400, Valdis.Kletnieks at vt.edu wrote:
> >> On Fri, 07 Oct 2011 16:12:45 MDT, Grant Likely said:
> >> > On Fri, Oct 7, 2011 at 12:43 AM, Greg KH <greg@kroah.com> wrote:
> >> > > On Fri, Oct 07, 2011 at 10:33:06AM +0500, G, Manjunath Kondaiah wrote:
> >>
> >> > >> +#define EPROBE_DEFER 517 ? ? /* restart probe again after some time */
> >> > >
> >> > > Can we really do this?
> >>
> >> > According to Arnd, yes this is okay.
> >>
> >> > > ?Isn't this some user/kernel api here?
> >>
> >> > > What's wrong with just "overloading" on top of an existing error code?
> >> > > Surely one of the other 516 types could be used here, right?
> >>
> >> > overloading makes it really hard to find the users at a later date.
> >>
> >> Would proposing '#define EPROBE_DEFER EAGAIN' be acceptable to everybody? That
> >> would allow overloading EAGAIN, but still make it easy to tell the usages apart
> >> if we need to separate them later...
> >
> > Yes, please do that, it is what USB does for it's internal error code
> > handling.
> 
> Really?  When we've only currently used approximately 2^9 of a 2^31
> numberspace?  I'm fine with making sure that the number doesn't show
> up in the userspace headers, but it makes no sense to overload the
> #defines.  Particularly so in this case where it isn't feasible to
> audit every driver to figure out what probe might possibly return.  It
> is well within the realm of possibility that existing drivers are
> already returning -EAGAIN.

I doubt they are, but you are right, it's really hard to tell.

> Besides; linux/errno.h *already* has linux-internal error codes that
> do not get exported out to userspace.  There is an #ifdef __KERNEL__
> block around ERESTARTSYS through EIOCBRETRY which is filtered out when
> exporting headers.  I can't see any possible reason why we wouldn't
> add Linux internal error codes here.

As long as it stays internal, that's fine, I was worried that this would
be exported to userspace.

Alan, still object to this?

greg k-h

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox