LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] IB/ehca: Serialize HCA-related hCalls if necessary
From: Joachim Fenkes @ 2007-12-10 17:59 UTC (permalink / raw)
  To: LinuxPPC-Dev, LKML, OF-General, Roland Dreier, OF-EWG,
	Arnd Bergmann
  Cc: Stefan Roscher, Christoph Raisch, Marcus Eder

Several pSeries firmware versions share a rare locking issue in the
HCA-related hCalls. Check for a feature flag that indicates the issue being
fixed and serialize all HCA hCalls if not.

Signed-off-by: Joachim Fenkes <fenkes@de.ibm.com>
---

This is the revised version of my previous patch, which does not rely on the
processor version any longer.

 drivers/infiniband/hw/ehca/ehca_main.c |   13 +++++++++++++
 drivers/infiniband/hw/ehca/hcp_if.c    |   28 +++++++++++-----------------
 drivers/infiniband/hw/ehca/hipz_hw.h   |    1 +
 3 files changed, 25 insertions(+), 17 deletions(-)

diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index 90d4334..c7bff3e 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -43,6 +43,7 @@
 #ifdef CONFIG_PPC_64K_PAGES
 #include <linux/slab.h>
 #endif
+
 #include "ehca_classes.h"
 #include "ehca_iverbs.h"
 #include "ehca_mrmw.h"
@@ -66,6 +67,7 @@ int ehca_poll_all_eqs  = 1;
 int ehca_static_rate   = -1;
 int ehca_scaling_code  = 0;
 int ehca_mr_largepage  = 1;
+int ehca_lock_hcalls   = -1;
 
 module_param_named(open_aqp1,     ehca_open_aqp1,     int, S_IRUGO);
 module_param_named(debug_level,   ehca_debug_level,   int, S_IRUGO);
@@ -77,6 +79,7 @@ module_param_named(poll_all_eqs,  ehca_poll_all_eqs,  int, S_IRUGO);
 module_param_named(static_rate,   ehca_static_rate,   int, S_IRUGO);
 module_param_named(scaling_code,  ehca_scaling_code,  int, S_IRUGO);
 module_param_named(mr_largepage,  ehca_mr_largepage,  int, S_IRUGO);
+module_param_named(lock_hcalls,   ehca_lock_hcalls,   bool, S_IRUGO);
 
 MODULE_PARM_DESC(open_aqp1,
 		 "AQP1 on startup (0: no (default), 1: yes)");
@@ -102,6 +105,9 @@ MODULE_PARM_DESC(scaling_code,
 MODULE_PARM_DESC(mr_largepage,
 		 "use large page for MR (0: use PAGE_SIZE (default), "
 		 "1: use large page depending on MR size");
+MODULE_PARM_DESC(lock_hcalls,
+		 "serialize all hCalls made by the driver "
+		 "(default: autodetect)");
 
 DEFINE_RWLOCK(ehca_qp_idr_lock);
 DEFINE_RWLOCK(ehca_cq_idr_lock);
@@ -258,6 +264,7 @@ static struct cap_descr {
 	{ HCA_CAP_UD_LL_QP, "HCA_CAP_UD_LL_QP" },
 	{ HCA_CAP_RESIZE_MR, "HCA_CAP_RESIZE_MR" },
 	{ HCA_CAP_MINI_QP, "HCA_CAP_MINI_QP" },
+	{ HCA_CAP_H_ALLOC_RES_SYNC, "HCA_CAP_H_ALLOC_RES_SYNC" },
 };
 
 static int ehca_sense_attributes(struct ehca_shca *shca)
@@ -333,6 +340,12 @@ static int ehca_sense_attributes(struct ehca_shca *shca)
 		if (EHCA_BMASK_GET(hca_cap_descr[i].mask, shca->hca_cap))
 			ehca_gen_dbg("   %s", hca_cap_descr[i].descr);
 
+	/* Autodetect hCall locking -- the "H_ALLOC_RESOURCE synced" flag is
+	 * a firmware property, so it's valid across all adapters
+	 */
+	if (ehca_lock_hcalls == -1)
+		ehca_lock_hcalls = !(shca->hca_cap & HCA_CAP_H_ALLOC_RES_SYNC);
+
 	/* translate supported MR page sizes; always support 4K */
 	shca->hca_cap_mr_pgsize = EHCA_PAGESIZE;
 	if (ehca_mr_largepage) { /* support extra sizes only if enabled */
diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c
index c16a213..331b5e8 100644
--- a/drivers/infiniband/hw/ehca/hcp_if.c
+++ b/drivers/infiniband/hw/ehca/hcp_if.c
@@ -89,6 +89,7 @@
 #define HCALL9_REGS_FORMAT HCALL7_REGS_FORMAT " r11=%lx r12=%lx"
 
 static DEFINE_SPINLOCK(hcall_lock);
+extern int ehca_lock_hcalls;
 
 static u32 get_longbusy_msecs(int longbusy_rc)
 {
@@ -120,26 +121,21 @@ static long ehca_plpar_hcall_norets(unsigned long opcode,
 				    unsigned long arg7)
 {
 	long ret;
-	int i, sleep_msecs, do_lock;
-	unsigned long flags;
+	int i, sleep_msecs;
+	unsigned long flags = 0;
 
 	ehca_gen_dbg("opcode=%lx " HCALL7_REGS_FORMAT,
 		     opcode, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
 
-	/* lock H_FREE_RESOURCE(MR) against itself and H_ALLOC_RESOURCE(MR) */
-	if ((opcode == H_FREE_RESOURCE) && (arg7 == 5)) {
-		arg7 = 0; /* better not upset firmware */
-		do_lock = 1;
-	}
-
 	for (i = 0; i < 5; i++) {
-		if (do_lock)
+		/* serialize hCalls to work around firmware issue */
+		if (ehca_lock_hcalls)
 			spin_lock_irqsave(&hcall_lock, flags);
 
 		ret = plpar_hcall_norets(opcode, arg1, arg2, arg3, arg4,
 					 arg5, arg6, arg7);
 
-		if (do_lock)
+		if (ehca_lock_hcalls)
 			spin_unlock_irqrestore(&hcall_lock, flags);
 
 		if (H_IS_LONG_BUSY(ret)) {
@@ -174,24 +170,22 @@ static long ehca_plpar_hcall9(unsigned long opcode,
 			      unsigned long arg9)
 {
 	long ret;
-	int i, sleep_msecs, do_lock;
+	int i, sleep_msecs;
 	unsigned long flags = 0;
 
 	ehca_gen_dbg("INPUT -- opcode=%lx " HCALL9_REGS_FORMAT, opcode,
 		     arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
 
-	/* lock H_ALLOC_RESOURCE(MR) against itself and H_FREE_RESOURCE(MR) */
-	do_lock = ((opcode == H_ALLOC_RESOURCE) && (arg2 == 5));
-
 	for (i = 0; i < 5; i++) {
-		if (do_lock)
+		/* serialize hCalls to work around firmware issue */
+		if (ehca_lock_hcalls)
 			spin_lock_irqsave(&hcall_lock, flags);
 
 		ret = plpar_hcall9(opcode, outs,
 				   arg1, arg2, arg3, arg4, arg5,
 				   arg6, arg7, arg8, arg9);
 
-		if (do_lock)
+		if (ehca_lock_hcalls)
 			spin_unlock_irqrestore(&hcall_lock, flags);
 
 		if (H_IS_LONG_BUSY(ret)) {
@@ -821,7 +815,7 @@ u64 hipz_h_free_resource_mr(const struct ipz_adapter_handle adapter_handle,
 	return ehca_plpar_hcall_norets(H_FREE_RESOURCE,
 				       adapter_handle.handle,    /* r4 */
 				       mr->ipz_mr_handle.handle, /* r5 */
-				       0, 0, 0, 0, 5);
+				       0, 0, 0, 0, 0);
 }
 
 u64 hipz_h_reregister_pmr(const struct ipz_adapter_handle adapter_handle,
diff --git a/drivers/infiniband/hw/ehca/hipz_hw.h b/drivers/infiniband/hw/ehca/hipz_hw.h
index 485b840..bf996c7 100644
--- a/drivers/infiniband/hw/ehca/hipz_hw.h
+++ b/drivers/infiniband/hw/ehca/hipz_hw.h
@@ -378,6 +378,7 @@ struct hipz_query_hca {
 #define HCA_CAP_UD_LL_QP              EHCA_BMASK_IBM(16, 16)
 #define HCA_CAP_RESIZE_MR             EHCA_BMASK_IBM(17, 17)
 #define HCA_CAP_MINI_QP               EHCA_BMASK_IBM(18, 18)
+#define HCA_CAP_H_ALLOC_RES_SYNC      EHCA_BMASK_IBM(19, 19)
 
 /* query port response block */
 struct hipz_query_port {
-- 
1.5.2

^ permalink raw reply related

* Re: [PATCH] IB/ehca: Serialize HCA-related hCalls on POWER5
From: Joachim Fenkes @ 2007-12-10 17:59 UTC (permalink / raw)
  To: Arnd Bergmann, OF-EWG, OF-General, LKML, linuxppc-dev,
	Roland Dreier, Roland Dreier
  Cc: Christoph Raisch, Stefan Roscher, Marcus Eder
In-Reply-To: <OF85E31FAA.DADA6039-ONC12573AA.005439C8-C12573AA.005A132E@LocalDomain>

Hi, guys,

> We're taking this to the firmware architects at the moment, but they're 
not 
> very fond of the idea of reporting the absence of bugs through 
capability 
> flags, as this could quickly lead to the exhaustion of flag bits. We'll 
let 
> the discussion stew for a bit, but if we don't get this flag, we'll have 
to 
> resort to the CPU features.

The architects have spoken, and we're getting a capability flag for this. 
I'll repost my patch with new autodetection code that doesn't involve 
checking the processor version.
 
> >  > Regarding the performance problem, have you checked whether 
converting all
> >  > your spin_lock_irqsave to spin_lock/spin_lock_irq improves your 
performance
> >  > on the older machines? Maybe it's already fast enough that way.
> > 
> > It does seem that the only places that the hcall_lock is taken also
> > use msleep, so they must always be in process context.  So you can
> > safely just use spin_lock(), right?
>
> As Arnd said, there are hCalls that will never return H_LONG_BUSY_*, 
such as 
> H_QUERY_PORT and chums, so they will never sleep. The surrounding 
functions, 
> though, are not prepared to be called from interrupt context (GFP_KERNEL 
comes
> to mind), so I agree that a simple spin_lock() will suffice. Thanks, 
Arnd, for
> pointing this out.

As I pointed out in my earlier mail, there's still an issue with 
map_phys_fmr possibly sleeping. Let's keep the irqsave for the time being 
and revisit this part once we find a solution to map_phys_fmr.

Regards,
  Joachim

^ permalink raw reply

* Re: [PATCH] IB/ehca: Serialize HCA-related hCalls on POWER5
From: Joachim Fenkes @ 2007-12-10 17:41 UTC (permalink / raw)
  To: Roland Dreier
  Cc: Arnd Bergmann, LKML, OF-EWG, linuxppc-dev, Christoph Raisch,
	Marcus Eder, OF-General, Stefan Roscher
In-Reply-To: <adaodcza1xc.fsf@cisco.com>

On Monday 10 December 2007 00:22, Roland Dreier wrote:
> Fair enough... according to Documentation/infiniband/core_locking.txt,
> the only driver methods that cannot sleep are:
> 
>     [...]
>     map_phys_fmr

In fact, we do use hCalls there. Our hardware doesn't actually support FMRs,
so we translate a "map FMR" into a "reallocate PMR", which doesn't work
without hCalls. What's more, the hCalls involved (e.g. H_FREE_RESOURCE)
might well return H_LONG_BUSY, so the whole operation might sleep; no way
around it.

How should we deal with this?

Thanks,
  Joachim

^ permalink raw reply

* Re: [PATCH 2/3] Use embedded libfdt in the bootwrapper
From: Scott Wood @ 2007-12-10 17:32 UTC (permalink / raw)
  To: David Gibson; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <20071210032839.71412DDDFA@ozlabs.org>

On Mon, Dec 10, 2007 at 02:28:39PM +1100, David Gibson wrote:
> +#define check_err(err) \
> +	({ \
> +		if (BAD_ERROR(err) || ((err < 0) && DEBUG)) \
> +			printf("%s():%d  %s\n\r", __FUNCTION__, __LINE__, \
> +			       fdt_strerror(err)); \
> +		if (BAD_ERROR(err)) \
> +			exit(); \
> +		(err < 0) ? -1 : 0; \
> +	})
> +
> +#define offset_devp(off)	\
> +	({ \
> +		int offset = (off); \
> +		check_err(offset) ? NULL : (void *)(offset+1); \
> +	})
> +
> +#define devp_offset(devp)	(((int)(devp))-1)

How does using offsets as devps work if a devp was previously acquired to a
node that has to be moved due to a change later made in an earlier part of
the tree?

-Scott

^ permalink raw reply

* Re: [PATCH] windfarm: add PowerMac 12,1 support
From: Étienne Bersac @ 2007-12-10 17:22 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev
In-Reply-To: <1197307026.8825.7.camel@thilivren>

Hi,

Appended a patch which builds without warnings due to struct declared
const. Please review it.

Thanks,
Étienne

Signed-off-by: Étienne Bersac <bersace03@gmail.com>
---
Implement fan control for PowerMac 12,1 machines. This needs update to
windfarm_lm75 and windfarm_max6690 sensors drivers in order to properly
match sensors by their location. The code is based on windfarm_pm81
driver, adapted to the complexity of PowerMac 12,1 : 4 system control
loops instead of one, per-control target correction and per-model
control rubber-banding.

All the data are copied from Darwin 8.10 AppleMacRISC4PE-185.0.0 module
as well as correction formula.

diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig
index 3673dd2..4c048b3 100644
--- a/arch/powerpc/configs/g5_defconfig
+++ b/arch/powerpc/configs/g5_defconfig
@@ -667,6 +667,7 @@ CONFIG_WINDFARM=y
 CONFIG_WINDFARM_PM81=y
 CONFIG_WINDFARM_PM91=y
 CONFIG_WINDFARM_PM112=y
+CONFIG_WINDFARM_PM121=y
 # CONFIG_PMAC_RACKMETER is not set
 CONFIG_NETDEVICES=y
 # CONFIG_NETDEVICES_MULTIQUEUE is not set
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index 77f50b6..2d028e6 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -234,6 +234,14 @@ config WINDFARM_PM112
 	  which are the recent dual and quad G5 machines using the
 	  970MP dual-core processor.
 
+config WINDFARM_PM121
+	tristate "Support for thermal management on PowerMac12,1"
+	depends on WINDFARM && I2C && PMAC_SMU
+	select I2C_POWERMAC
+	help
+	  This driver provides thermal control for the PowerMac12,1
+	  which is the iMac G5 (iSight) 970MP
+
 config ANSLCD
 	tristate "Support for ANS LCD display"
 	depends on ADB_CUDA && PPC_PMAC
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index 2dfc3f4..e3132ef 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -42,4 +42,9 @@ obj-$(CONFIG_WINDFARM_PM112)	+= windfarm_pm112.o windfarm_smu_sat.o \
 				   windfarm_smu_sensors.o \
 				   windfarm_max6690_sensor.o \
 				   windfarm_lm75_sensor.o windfarm_pid.o
+obj-$(CONFIG_WINDFARM_PM121)	+= windfarm_pm121.o windfarm_smu_sat.o \
+				   windfarm_smu_controls.o \
+				   windfarm_smu_sensors.o \
+				   windfarm_max6690_sensor.o \
+				   windfarm_lm75_sensor.o windfarm_pid.o
 obj-$(CONFIG_PMAC_RACKMETER)	+= rack-meter.o
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index 7e10c3a..b92b959 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -127,6 +127,12 @@ static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
 	 */
 	if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY"))
 		lm->sens.name = "hd-temp";
+	else if (!strcmp(loc, "Incoming Air Temp"))
+		lm->sens.name = "incoming-air-temp";
+	else if (!strcmp(loc, "ODD Temp"))
+		lm->sens.name = "optical-drive-temp";
+	else if (!strcmp(loc, "HD Temp"))
+		lm->sens.name = "hard-drive-temp";
 	else
 		goto fail;
 
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c
index 5f03aab..e207a90 100644
--- a/drivers/macintosh/windfarm_max6690_sensor.c
+++ b/drivers/macintosh/windfarm_max6690_sensor.c
@@ -77,18 +77,28 @@ static struct wf_sensor_ops wf_max6690_ops = {
 	.owner		= THIS_MODULE,
 };
 
-static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr)
+static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr,
+			      const char *loc)
 {
 	struct wf_6690_sensor *max;
-	char *name = "backside-temp";
+	char *name;
 
 	max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
 	if (max == NULL) {
 		printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: "
-		       "no memory\n", name);
+		       "no memory\n", loc);
 		return;
 	}
 
+	if (!strcmp(loc, "BACKSIDE"))
+		name = "backside-temp";
+	else if (!strcmp(loc, "NB Ambient"))
+		name = "north-bridge-temp";
+	else if (!strcmp(loc, "GPU Ambient"))
+		name = "gpu-temp";
+	else
+		goto fail;
+
 	max->sens.ops = &wf_max6690_ops;
 	max->sens.name = name;
 	max->i2c.addr = addr >> 1;
@@ -138,9 +148,7 @@ static int wf_max6690_attach(struct i2c_adapter *adapter)
 		if (loc == NULL || addr == 0)
 			continue;
 		printk("found max6690, loc=%s addr=0x%02x\n", loc, addr);
-		if (strcmp(loc, "BACKSIDE"))
-			continue;
-		wf_max6690_create(adapter, addr);
+		wf_max6690_create(adapter, addr, loc);
 	}
 
 	return 0;
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
new file mode 100644
index 0000000..5276156
--- /dev/null
+++ b/drivers/macintosh/windfarm_pm121.c
@@ -0,0 +1,1040 @@
+/*
+ * Windfarm PowerMac thermal control. iMac G5 iSight
+ *
+ * (c) Copyright 2007 Étienne Bersac <bersace@gmail.com>
+ *
+ * Bits & pieces from windfarm_pm81.c by (c) Copyright 2005 Benjamin
+ * Herrenschmidt, IBM Corp. <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ *
+ *
+ * PowerMac12,1
+ * ============
+ *
+ *
+ * The algorithm used is the PID control algorithm, used the same way
+ * the published Darwin code does, using the same values that are
+ * present in the Darwin 8.10 snapshot property lists (note however
+ * that none of the code has been re-used, it's a complete
+ * re-implementation
+ *
+ * There is two models using PowerMac12,1. Model 2 is iMac G5 iSight
+ * 17" while Model 3 is iMac G5 20". They do have both the same
+ * controls with a tiny difference. The control-ids of hard-drive-fan
+ * and cpu-fan is swapped.
+ *
+ *
+ * Target Correction :
+ *
+ * controls have a target correction calculated as :
+ *
+ * new_min = ((((average_power * slope) >> 16) + offset) >> 16) + min_value
+ * new_value = max(new_value, max(new_min, 0))
+ *
+ * OD Fan control correction.
+ *
+ * # model_id: 2
+ *   offset		: -19563152
+ *   slope		:  1956315
+ *
+ * # model_id: 3
+ *   offset		: -15650652
+ *   slope		:  1565065
+ *
+ * HD Fan control correction.
+ *
+ * # model_id: 2
+ *   offset		: -15650652
+ *   slope		:  1565065
+ *
+ * # model_id: 3
+ *   offset		: -19563152
+ *   slope		:  1956315
+ *
+ * CPU Fan control correction.
+ *
+ * # model_id: 2
+ *   offset		: -25431900
+ *   slope		:  2543190
+ *
+ * # model_id: 3
+ *   offset		: -15650652
+ *   slope		:  1565065
+ *
+ *
+ * Target rubber-banding :
+ *
+ * Some controls have a target correction which depends on another
+ * control value. The correction is computed in the following way :
+ *
+ * new_min = ref_value * slope + offset
+ *
+ * ref_value is the value of the reference control. If new_min is
+ * greater than 0, then we correct the target value using :
+ *
+ * new_target = max (new_target, new_min >> 16)
+ *
+ *
+ * # model_id : 2
+ *   control	: cpu-fan
+ *   ref	: optical-drive-fan
+ *   offset	: -15650652
+ *   slope	: 1565065
+ *
+ * # model_id : 3
+ *   control	: optical-drive-fan
+ *   ref	: hard-drive-fan
+ *   offset	: -32768000
+ *   slope	: 65536
+ *
+ *
+ * In order to have the moste efficient correction with those
+ * dependencies, we must trigger HD loop before OD loop before CPU
+ * loop.
+ *
+ *
+ * The various control loops found in Darwin config file are:
+ *
+ * HD Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : hard-drive-fan
+ *   sensor         : hard-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x002D70A3
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x370000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : hard-drive-fan
+ *   sensor         : hard-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x002170A3
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x370000
+ *                    Interval = 5s
+ *
+ * OD Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : optical-drive-fan
+ *   sensor         : optical-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x001FAE14
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x320000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : optical-drive-fan
+ *   sensor         : optical-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x001FAE14
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x320000
+ *                    Interval = 5s
+ *
+ * GPU Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : hard-drive-fan
+ *   sensor         : gpu-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x002A6666
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x5A0000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : cpu-fan
+ *   sensor         : gpu-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x0010CCCC
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x500000
+ *                    Interval = 5s
+ *
+ * KODIAK (aka northbridge) Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : optical-drive-fan
+ *   sensor         : north-bridge-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x003BD70A
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x550000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : hard-drive-fan
+ *   sensor         : north-bridge-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x0030F5C2
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x550000
+ *                    Interval = 5s
+ *
+ * CPU Fan control loop.
+ *
+ *   control        : cpu-fan
+ *   sensors        : cpu-temp, cpu-power
+ *   PID params     : from SDB partition
+ *
+ *
+ * CPU Slew control loop.
+ *
+ *   control        : cpufreq-clamp
+ *   sensor         : cpu-temp
+ *
+ */
+
+#undef	DEBUG
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/kmod.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+
+#define VERSION "0.3"
+
+static int pm121_mach_model;	/* machine model id */
+
+/* Controls & sensors */
+static struct wf_sensor	*sensor_cpu_power;
+static struct wf_sensor	*sensor_cpu_temp;
+static struct wf_sensor	*sensor_cpu_voltage;
+static struct wf_sensor	*sensor_cpu_current;
+static struct wf_sensor	*sensor_gpu_temp;
+static struct wf_sensor	*sensor_north_bridge_temp;
+static struct wf_sensor	*sensor_hard_drive_temp;
+static struct wf_sensor	*sensor_optical_drive_temp;
+static struct wf_sensor	*sensor_incoming_air_temp; /* unused ! */
+
+enum {
+	FAN_CPU,
+	FAN_HD,
+	FAN_OD,
+	CPUFREQ,
+	N_CONTROLS
+};
+static struct wf_control *controls[N_CONTROLS] = {};
+
+/* Set to kick the control loop into life */
+static int pm121_all_controls_ok, pm121_all_sensors_ok, pm121_started;
+
+enum {
+	FAILURE_FAN		= 1 << 0,
+	FAILURE_SENSOR		= 1 << 1,
+	FAILURE_OVERTEMP	= 1 << 2
+};
+
+/* All sys loops. Note the HD before the OD loop in order to have it
+   run before. */
+enum {
+	LOOP_GPU,		/* control = hd or cpu, but luckily,
+				   it doesn't matter */
+	LOOP_HD,		/* control = hd */
+	LOOP_KODIAK,		/* control = hd or od */
+	LOOP_OD,		/* control = od */
+	N_LOOPS
+};
+
+static const char *loop_names[N_LOOPS] = {
+	"GPU",
+	"HD",
+	"KODIAK",
+	"OD",
+};
+
+#define	PM121_NUM_CONFIGS	2
+
+static unsigned int pm121_failure_state;
+static int pm121_readjust, pm121_skipping;
+static s32 average_power;
+
+struct pm121_correction {
+	int	offset;
+	int	slope;
+};
+
+static struct pm121_correction corrections[N_CONTROLS][PM121_NUM_CONFIGS] = {
+	/* FAN_OD */
+	{
+		/* MODEL 2 */
+		{ .offset	= -19563152,
+		  .slope	=  1956315
+		},
+		/* MODEL 3 */
+		{ .offset	= -15650652,
+		  .slope	=  1565065
+		},
+	},
+	/* FAN_HD */
+	{
+		/* MODEL 2 */
+		{ .offset	= -15650652,
+		  .slope	=  1565065
+		},
+		/* MODEL 3 */
+		{ .offset	= -19563152,
+		  .slope	=  1956315
+		},
+	},
+	/* FAN_CPU */
+	{
+		/* MODEL 2 */
+		{ .offset	= -25431900,
+		  .slope	=  2543190
+		},
+		/* MODEL 3 */
+		{ .offset	= -15650652,
+		  .slope	=  1565065
+		},
+	},
+	/* CPUFREQ has no correction (and is not implemented at all) */
+};
+
+struct pm121_connection {
+	unsigned int	control_id;
+	unsigned int	ref_id;
+	struct pm121_correction	correction;
+};
+
+static struct pm121_connection pm121_connections[] = {
+	/* MODEL 2 */
+	{ .control_id	= FAN_CPU,
+	  .ref_id	= FAN_OD,
+	  { .offset	= -32768000,
+	    .slope	=  65536
+	  }
+	},
+	/* MODEL 3 */
+	{ .control_id	= FAN_OD,
+	  .ref_id	= FAN_HD,
+	  { .offset	= -32768000,
+	    .slope	=  65536
+	  }
+	},
+};
+
+/* pointer to the current model connection */
+static struct pm121_connection *pm121_connection;
+
+/*
+ * ****** System Fans Control Loop ******
+ *
+ */
+
+/* Since each loop handles only one control and we want to avoid
+ * writing virtual control, we store the control correction with the
+ * loop params. Some data are not set, there are common to all loop
+ * and thus, hardcoded.
+ */
+struct pm121_sys_param {
+	/* purely informative since we use mach_model-2 as index */
+	int			model_id;
+	struct wf_sensor	**sensor; /* use sensor_id instead ? */
+	s32			gp, itarget;
+	unsigned int		control_id;
+};
+
+static struct pm121_sys_param
+pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] = {
+	/* GPU Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_gpu_temp,
+		  .gp		= 0x002A6666,
+		  .itarget	= 0x5A0000,
+		  .control_id	= FAN_HD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_gpu_temp,
+		  .gp		= 0x0010CCCC,
+		  .itarget	= 0x500000,
+		  .control_id	= FAN_CPU,
+		},
+	},
+	/* HD Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_hard_drive_temp,
+		  .gp		= 0x002D70A3,
+		  .itarget	= 0x370000,
+		  .control_id	= FAN_HD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_hard_drive_temp,
+		  .gp		= 0x002170A3,
+		  .itarget	= 0x370000,
+		  .control_id	= FAN_HD,
+		},
+	},
+	/* KODIAK Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_north_bridge_temp,
+		  .gp		= 0x003BD70A,
+		  .itarget	= 0x550000,
+		  .control_id	= FAN_OD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_north_bridge_temp,
+		  .gp		= 0x0030F5C2,
+		  .itarget	= 0x550000,
+		  .control_id	= FAN_HD,
+		},
+	},
+	/* OD Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_optical_drive_temp,
+		  .gp		= 0x001FAE14,
+		  .itarget	= 0x320000,
+		  .control_id	= FAN_OD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_optical_drive_temp,
+		  .gp		= 0x001FAE14,
+		  .itarget	= 0x320000,
+		  .control_id	= FAN_OD,
+		},
+	},
+};
+
+/* the hardcoded values */
+#define	PM121_SYS_GD		0x00000000
+#define	PM121_SYS_GR		0x00019999
+#define	PM121_SYS_HISTORY_SIZE	2
+#define	PM121_SYS_INTERVAL	5
+
+/* State data used by the system fans control loop
+ */
+struct pm121_sys_state {
+	int			ticks;
+	s32			setpoint;
+	struct wf_pid_state	pid;
+};
+
+struct pm121_sys_state *pm121_sys_state[N_LOOPS] = {};
+
+/*
+ * ****** CPU Fans Control Loop ******
+ *
+ */
+
+#define PM121_CPU_INTERVAL	1
+
+/* State data used by the cpu fans control loop
+ */
+struct pm121_cpu_state {
+	int			ticks;
+	s32			setpoint;
+	struct wf_cpu_pid_state	pid;
+};
+
+static struct pm121_cpu_state *pm121_cpu_state;
+
+
+
+/*
+ * ***** Implementation *****
+ *
+ */
+
+/* correction the value using the output-low-bound correction algo */
+static s32 pm121_correct(s32 new_setpoint,
+			 unsigned int control_id,
+			 s32 min)
+{
+	s32 new_min;
+	struct pm121_correction *correction;
+	correction = &corrections[control_id][pm121_mach_model - 2];
+
+	new_min = (average_power * correction->slope) >> 16;
+	new_min += correction->offset;
+	new_min = (new_min >> 16) + min;
+
+	return max(new_setpoint, max(new_min, 0));
+}
+
+static s32 pm121_connect(unsigned int control_id, s32 setpoint)
+{
+	s32 new_min, value, new_setpoint;
+
+	if (pm121_connection->control_id == control_id) {
+		controls[control_id]->ops->get_value(controls[control_id],
+						     &value);
+		new_min = value * pm121_connection->correction.slope;
+		new_min += pm121_connection->correction.offset;
+		if (new_min > 0) {
+			new_setpoint = max(setpoint, (new_min >> 16));
+			if (new_setpoint != setpoint) {
+				pr_debug("pm121: %s depending on %s, "
+					 "corrected from %d to %d RPM\n",
+					 controls[control_id]->name,
+					 controls[pm121_connection->ref_id]->name,
+					 (int) setpoint, (int) new_setpoint);
+			}
+		} else
+			new_setpoint = setpoint;
+	}
+	/* no connection */
+	else
+		new_setpoint = setpoint;
+
+	return new_setpoint;
+}
+
+/* FAN LOOPS */
+static void pm121_create_sys_fans(int loop_id)
+{
+	struct pm121_sys_param *param = NULL;
+	struct wf_pid_param pid_param;
+	struct wf_control *control = NULL;
+	int i;
+
+	/* First, locate the params for this model */
+	for (i = 0; i < PM121_NUM_CONFIGS; i++) {
+		if (pm121_sys_all_params[loop_id][i].model_id == pm121_mach_model) {
+			param = &(pm121_sys_all_params[loop_id][i]);
+			break;
+		}
+	}
+
+	/* No params found, put fans to max */
+	if (param == NULL) {
+		printk(KERN_WARNING "pm121: %s fan config not found "
+		       " for this machine model\n",
+		       loop_names[loop_id]);
+		goto fail;
+	}
+
+	control = controls[param->control_id];
+
+	/* Alloc & initialize state */
+	pm121_sys_state[loop_id] = kmalloc(sizeof(struct pm121_sys_state),
+					   GFP_KERNEL);
+	if (pm121_sys_state[loop_id] == NULL) {
+		printk(KERN_WARNING "pm121: Memory allocation error\n");
+		goto fail;
+	}
+	pm121_sys_state[loop_id]->ticks = 1;
+
+	/* Fill PID params */
+	pid_param.gd		= PM121_SYS_GD;
+	pid_param.gp		= param->gp;
+	pid_param.gr		= PM121_SYS_GR;
+	pid_param.interval	= PM121_SYS_INTERVAL;
+	pid_param.history_len	= PM121_SYS_HISTORY_SIZE;
+	pid_param.itarget	= param->itarget;
+	pid_param.min		= control->ops->get_min(control);
+	pid_param.max		= control->ops->get_max(control);
+
+	wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param);
+
+	pr_debug("pm121: %s Fan control loop initialized.\n"
+		 "       itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
+		 loop_names[loop_id], FIX32TOPRINT(pid_param.itarget),
+		 pid_param.min, pid_param.max);
+	return;
+
+ fail:
+	/* note that this is not optimal since another loop may still
+	   control the same control */
+	printk(KERN_WARNING "pm121: failed to set up %s loop "
+	       "setting \"%s\" to max speed.\n",
+	       loop_names[loop_id], control->name);
+
+	if (control)
+		wf_control_set_max(control);
+}
+
+static void pm121_sys_fans_tick(int loop_id)
+{
+	struct pm121_sys_param *param;
+	struct pm121_sys_state *st;
+	struct wf_sensor *sensor;
+	struct wf_control *control;
+	s32 temp, new_setpoint;
+	int rc;
+
+	param = &(pm121_sys_all_params[loop_id][pm121_mach_model-2]);
+	st = pm121_sys_state[loop_id];
+	sensor = *(param->sensor);
+	control = controls[param->control_id];
+
+	if (--st->ticks != 0) {
+		if (pm121_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = PM121_SYS_INTERVAL;
+
+	rc = sensor->ops->get_value(sensor, &temp);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: %s sensor error %d\n",
+		       sensor->name, rc);
+		pm121_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	pr_debug("pm121: %s Fan tick ! %s: %d.%03d\n",
+		 loop_names[loop_id], sensor->name,
+		 FIX32TOPRINT(temp));
+
+	new_setpoint = wf_pid_run(&st->pid, temp);
+
+	/* correction */
+	new_setpoint = pm121_correct(new_setpoint,
+				     param->control_id,
+				     st->pid.param.min);
+	/* linked corretion */
+	new_setpoint = pm121_connect(param->control_id, new_setpoint);
+
+	if (new_setpoint == st->setpoint)
+		return;
+	st->setpoint = new_setpoint;
+	pr_debug("pm121: %s corrected setpoint: %d RPM\n",
+		 control->name, (int)new_setpoint);
+ readjust:
+	if (control && pm121_failure_state == 0) {
+		rc = control->ops->set_value(control, st->setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: %s fan error %d\n",
+			       control->name, rc);
+			pm121_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+
+/* CPU LOOP */
+static void pm121_create_cpu_fans(void)
+{
+	struct wf_cpu_pid_param pid_param;
+	const struct smu_sdbp_header *hdr;
+	struct smu_sdbp_cpupiddata *piddata;
+	struct smu_sdbp_fvt *fvt;
+	struct wf_control *fan_cpu;
+	s32 tmax, tdelta, maxpow, powadj;
+
+	fan_cpu = controls[FAN_CPU];
+
+	/* First, locate the PID params in SMU SBD */
+	hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
+	if (hdr == 0) {
+		printk(KERN_WARNING "pm121: CPU PID fan config not found.\n");
+		goto fail;
+	}
+	piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
+
+	/* Get the FVT params for operating point 0 (the only supported one
+	 * for now) in order to get tmax
+	 */
+	hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
+	if (hdr) {
+		fvt = (struct smu_sdbp_fvt *)&hdr[1];
+		tmax = ((s32)fvt->maxtemp) << 16;
+	} else
+		tmax = 0x5e0000; /* 94 degree default */
+
+	/* Alloc & initialize state */
+	pm121_cpu_state = kmalloc(sizeof(struct pm121_cpu_state),
+				  GFP_KERNEL);
+	if (pm121_cpu_state == NULL)
+		goto fail;
+	pm121_cpu_state->ticks = 1;
+
+	/* Fill PID params */
+	pid_param.interval = PM121_CPU_INTERVAL;
+	pid_param.history_len = piddata->history_len;
+	if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
+		printk(KERN_WARNING "pm121: History size overflow on "
+		       "CPU control loop (%d)\n", piddata->history_len);
+		pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
+	}
+	pid_param.gd = piddata->gd;
+	pid_param.gp = piddata->gp;
+	pid_param.gr = piddata->gr / pid_param.history_len;
+
+	tdelta = ((s32)piddata->target_temp_delta) << 16;
+	maxpow = ((s32)piddata->max_power) << 16;
+	powadj = ((s32)piddata->power_adj) << 16;
+
+	pid_param.tmax = tmax;
+	pid_param.ttarget = tmax - tdelta;
+	pid_param.pmaxadj = maxpow - powadj;
+
+	pid_param.min = fan_cpu->ops->get_min(fan_cpu);
+	pid_param.max = fan_cpu->ops->get_max(fan_cpu);
+
+	wf_cpu_pid_init(&pm121_cpu_state->pid, &pid_param);
+
+	pr_debug("pm121: CPU Fan control initialized.\n");
+	pr_debug("       ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM,\n",
+		 FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
+		 pid_param.min, pid_param.max);
+
+	return;
+
+ fail:
+	printk(KERN_WARNING "pm121: CPU fan config not found, max fan speed\n");
+
+	if (controls[CPUFREQ])
+		wf_control_set_max(controls[CPUFREQ]);
+	if (fan_cpu)
+		wf_control_set_max(fan_cpu);
+}
+
+
+static void pm121_cpu_fans_tick(struct pm121_cpu_state *st)
+{
+	s32 new_setpoint, temp, power;
+	struct wf_control *fan_cpu = NULL;
+	int rc;
+
+	if (--st->ticks != 0) {
+		if (pm121_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = PM121_CPU_INTERVAL;
+
+	fan_cpu = controls[FAN_CPU];
+
+	rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+	if (rc) {
+		printk(KERN_WARNING "pm121: CPU temp sensor error %d\n",
+		       rc);
+		pm121_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+	if (rc) {
+		printk(KERN_WARNING "pm121: CPU power sensor error %d\n",
+		       rc);
+		pm121_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	pr_debug("pm121: CPU Fans tick ! CPU temp: %d.%03d°C, power: %d.%03d\n",
+		 FIX32TOPRINT(temp), FIX32TOPRINT(power));
+
+	if (temp > st->pid.param.tmax)
+		pm121_failure_state |= FAILURE_OVERTEMP;
+
+	new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
+
+	/* correction */
+	new_setpoint = pm121_correct(new_setpoint,
+				     FAN_CPU,
+				     st->pid.param.min);
+
+	/* connected correction */
+	new_setpoint = pm121_connect(FAN_CPU, new_setpoint);
+
+	if (st->setpoint == new_setpoint)
+		return;
+	st->setpoint = new_setpoint;
+	pr_debug("pm121: CPU corrected setpoint: %d RPM\n", (int)new_setpoint);
+
+ readjust:
+	if (fan_cpu && pm121_failure_state == 0) {
+		rc = fan_cpu->ops->set_value(fan_cpu, st->setpoint);
+		if (rc) {
+			printk(KERN_WARNING "pm121: %s fan error %d\n",
+			       fan_cpu->name, rc);
+			pm121_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+/*
+ * ****** Common ******
+ *
+ */
+
+static void pm121_tick(void)
+{
+	unsigned int last_failure = pm121_failure_state;
+	unsigned int new_failure;
+	s32 total_power;
+	int i;
+
+	if (!pm121_started) {
+		pr_debug("pm121: creating control loops !\n");
+		for (i = 0; i < N_LOOPS; i++)
+			pm121_create_sys_fans(i);
+
+		pm121_create_cpu_fans();
+		pm121_started = 1;
+	}
+
+	/* skipping ticks */
+	if (pm121_skipping && --pm121_skipping)
+		return;
+
+	/* compute average power */
+	total_power = 0;
+	for (i = 0; i < pm121_cpu_state->pid.param.history_len; i++)
+		total_power += pm121_cpu_state->pid.powers[i];
+
+	average_power = total_power / pm121_cpu_state->pid.param.history_len;
+
+
+	pm121_failure_state = 0;
+	for (i = 0 ; i < N_LOOPS; i++) {
+		if (pm121_sys_state[i])
+			pm121_sys_fans_tick(i);
+	}
+
+	if (pm121_cpu_state)
+		pm121_cpu_fans_tick(pm121_cpu_state);
+
+	pm121_readjust = 0;
+	new_failure = pm121_failure_state & ~last_failure;
+
+	/* If entering failure mode, clamp cpufreq and ramp all
+	 * fans to full speed.
+	 */
+	if (pm121_failure_state && !last_failure) {
+		for (i = 0; i < N_CONTROLS; i++) {
+			if (controls[i])
+				wf_control_set_max(controls[i]);
+		}
+	}
+
+	/* If leaving failure mode, unclamp cpufreq and readjust
+	 * all fans on next iteration
+	 */
+	if (!pm121_failure_state && last_failure) {
+		if (controls[CPUFREQ])
+			wf_control_set_min(controls[CPUFREQ]);
+		pm121_readjust = 1;
+	}
+
+	/* Overtemp condition detected, notify and start skipping a couple
+	 * ticks to let the temperature go down
+	 */
+	if (new_failure & FAILURE_OVERTEMP) {
+		wf_set_overtemp();
+		pm121_skipping = 2;
+	}
+
+	/* We only clear the overtemp condition if overtemp is cleared
+	 * _and_ no other failure is present. Since a sensor error will
+	 * clear the overtemp condition (can't measure temperature) at
+	 * the control loop levels, but we don't want to keep it clear
+	 * here in this case
+	 */
+	if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+		wf_clear_overtemp();
+}
+
+
+static struct wf_control* pm121_register_control(struct wf_control *ct,
+						 const char *match,
+						 unsigned int id)
+{
+	if (controls[id] == NULL && !strcmp(ct->name, match)) {
+		if (wf_get_control(ct) == 0)
+			controls[FAN_OD] = ct;
+	}
+	return controls[id];
+}
+
+static void pm121_new_control(struct wf_control *ct)
+{
+	int all = 1;
+
+	if (pm121_all_controls_ok)
+		return;
+
+	all = all && pm121_register_control(ct, "optical-driver-fan", FAN_OD);
+	all = all && pm121_register_control(ct, "hard-driver-fan", FAN_HD);
+	all = all && pm121_register_control(ct, "cpu-driver-fan", FAN_CPU);
+	all = all && pm121_register_control(ct, "cpufreq-clamp", CPUFREQ);
+
+	if (all)
+		pm121_all_controls_ok = 1;
+}
+
+
+
+
+static struct wf_sensor* pm121_register_sensor(struct wf_sensor *sensor,
+					       const char *match,
+					       struct wf_sensor **var)
+{
+	if (*var == NULL && !strcmp(sensor->name, match)) {
+		if (wf_get_sensor(sensor) == 0)
+			*var = sensor;
+	}
+	return *var;
+}
+
+static void pm121_new_sensor(struct wf_sensor *sr)
+{
+	int all = 1;
+
+	if (pm121_all_sensors_ok)
+		return;
+
+	all = all && pm121_register_sensor(sr, "cpu-temp",
+					   &sensor_cpu_temp);
+	all = all && pm121_register_sensor(sr, "cpu-current",
+					   &sensor_cpu_current);
+	all = all && pm121_register_sensor(sr, "cpu-voltage",
+					   &sensor_cpu_voltage);
+	all = all && pm121_register_sensor(sr, "cpu-power",
+					   &sensor_cpu_power);
+	all = all && pm121_register_sensor(sr, "hard-drive-temp",
+					   &sensor_hard_drive_temp);
+	all = all && pm121_register_sensor(sr, "optical-drive-temp",
+					   &sensor_optical_drive_temp);
+	all = all && pm121_register_sensor(sr, "incoming-air-temp",
+					   &sensor_incoming_air_temp);
+	all = all && pm121_register_sensor(sr, "north-bridge-temp",
+					   &sensor_north_bridge_temp);
+	all = all && pm121_register_sensor(sr, "gpu-temp",
+					   &sensor_gpu_temp);
+
+	if (all)
+		pm121_all_sensors_ok = 1;
+}
+
+
+
+static int pm121_notify(struct notifier_block *self,
+			unsigned long event, void *data)
+{
+	switch (event) {
+	case WF_EVENT_NEW_CONTROL:
+		pr_debug("pm121: new control %s detected\n",
+			 ((struct wf_control *)data)->name);
+		pm121_new_control(data);
+		break;
+	case WF_EVENT_NEW_SENSOR:
+		pr_debug("pm121: new sensor %s detected\n",
+			 ((struct wf_sensor *)data)->name);
+		pm121_new_sensor(data);
+		break;
+	case WF_EVENT_TICK:
+		if (pm121_all_controls_ok && pm121_all_sensors_ok)
+			pm121_tick();
+		break;
+	}
+
+	return 0;
+}
+
+static struct notifier_block pm121_events = {
+	.notifier_call	= pm121_notify,
+};
+
+static int pm121_init_pm(void)
+{
+	const struct smu_sdbp_header *hdr;
+
+	hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
+	if (hdr != 0) {
+		struct smu_sdbp_sensortree *st =
+			(struct smu_sdbp_sensortree *)&hdr[1];
+		pm121_mach_model = st->model_id;
+	}
+
+	pm121_connection = &pm121_connections[pm121_mach_model - 2];
+
+	printk(KERN_INFO "pm121: Initializing for iMac G5 iSight model ID %d\n",
+	       pm121_mach_model);
+
+	return 0;
+}
+
+
+static int pm121_probe(struct platform_device *ddev)
+{
+	wf_register_client(&pm121_events);
+
+	return 0;
+}
+
+static int __devexit pm121_remove(struct platform_device *ddev)
+{
+	wf_unregister_client(&pm121_events);
+	return 0;
+}
+
+static struct platform_driver pm121_driver = {
+	.probe = pm121_probe,
+	.remove = __devexit_p(pm121_remove),
+	.driver = {
+		.name = "windfarm",
+		.bus = &platform_bus_type,
+	},
+};
+
+
+static int __init pm121_init(void)
+{
+	int rc = -ENODEV;
+
+	if (machine_is_compatible("PowerMac12,1"))
+		rc = pm121_init_pm();
+
+	if (rc == 0) {
+		request_module("windfarm_smu_controls");
+		request_module("windfarm_smu_sensors");
+		request_module("windfarm_smu_sat");
+		request_module("windfarm_lm75_sensor");
+		request_module("windfarm_max6690_sensor");
+		request_module("windfarm_cpufreq_clamp");
+		platform_driver_register(&pm121_driver);
+	}
+
+	return rc;
+}
+
+static void __exit pm121_exit(void)
+{
+
+	platform_driver_unregister(&pm121_driver);
+}
+
+
+module_init(pm121_init);
+module_exit(pm121_exit);
+
+MODULE_AUTHOR("Étienne Bersac <bersace@gmail.com>");
+MODULE_DESCRIPTION("Thermal control logic for iMac G5 (iSight)");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index 58c2590..961fa0e 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -218,6 +218,10 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
 		fct->ctrl.name = "cpu-fan";
 	else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
 		fct->ctrl.name = "drive-bay-fan";
+	else if (!strcmp(l, "HDD Fan")) /* seen on iMac G5 iSight */
+		fct->ctrl.name = "hard-drive-fan";
+	else if (!strcmp(l, "ODD Fan")) /* same */
+		fct->ctrl.name = "optical-drive-fan";
 
 	/* Unrecognized fan, bail out */
 	if (fct->ctrl.name == NULL)

^ permalink raw reply related

* Re: [i2c] [PATCH 0/4] Series to add device tree naming to i2c
From: Scott Wood @ 2007-12-10 16:42 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Jean Delvare, linuxppc-dev, i2c
In-Reply-To: <1197236326.6563.22.camel@pasglop>

On Mon, Dec 10, 2007 at 08:38:46AM +1100, Benjamin Herrenschmidt wrote:
> The more I think about it, the more I tend to agree that tagging isn't
> necessary and you are right. We should just match the name against the
> "compatible" property of the OF nodes (which mean we need to support
> multiple matches though since "compatible" is a list of strings).

It may not be strictly necessary, but I think it's a good idea not just for
safety reasons, but as an indication to the driver what additional
information it has access to.  We could put a match data pointer in the i2c
device, and have it be a valid node pointer if the match was an OF one (and
a device-specific struct for a straight platform device, etc).  This could
be useful if a device needs to have more properties than standard
address/type/interrupt for some reason.

-Scott

^ permalink raw reply

* Re: [PATCH] windfarm: add PowerMac 12,1 support
From: Étienne Bersac @ 2007-12-10 17:17 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev
In-Reply-To: <200712101639.52599.arnd@arndb.de>

Hi,

Thanks for reviewing the patch. I fix most of your comment excepts some
thing which i think are not mine. This is about the global variable. I
wrote this driver willing to be as more consistent with existing
windfarm_pm* drivers. All uses DBG(), global variable, etc. You'll see
that i dropped DBG in favor to pr_debug() ; thanks for pointing this.

windfarm drivers are plateform driver. This is why it uses global
variable, etc. This driver is never unloaded. (except for development
issues). Anyway, if Benjamin Herrenschmidt or Paul Mackerras tells to
use per module struct, i'll do so. The same goes for #ifdef MODULE.
That's just a copy paste from windfarm_pm81.c.

Appended the new patch, please review it.

Thanks,
Étienne.

Signed-off-by: Étienne Bersac <bersace03@laposte.net>
---

Implement fan control for PowerMac 12,1 machines. This needs update to
windfarm_lm75 and windfarm_max6690 sensors drivers in order to properly
match sensors by their location. The code is based on windfarm_pm81
driver, adapted to the complexity of PowerMac 12,1 : 4 system control
loops instead of one, per-control target correction and per-model
control rubber-banding.

All the data are copied from Darwin 8.10 AppleMacRISC4PE-185.0.0 module
as well as correction formula.

diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig
index 3673dd2..4c048b3 100644
--- a/arch/powerpc/configs/g5_defconfig
+++ b/arch/powerpc/configs/g5_defconfig
@@ -667,6 +667,7 @@ CONFIG_WINDFARM=y
 CONFIG_WINDFARM_PM81=y
 CONFIG_WINDFARM_PM91=y
 CONFIG_WINDFARM_PM112=y
+CONFIG_WINDFARM_PM121=y
 # CONFIG_PMAC_RACKMETER is not set
 CONFIG_NETDEVICES=y
 # CONFIG_NETDEVICES_MULTIQUEUE is not set
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index 77f50b6..2d028e6 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -234,6 +234,14 @@ config WINDFARM_PM112
 	  which are the recent dual and quad G5 machines using the
 	  970MP dual-core processor.
 
+config WINDFARM_PM121
+	tristate "Support for thermal management on PowerMac12,1"
+	depends on WINDFARM && I2C && PMAC_SMU
+	select I2C_POWERMAC
+	help
+	  This driver provides thermal control for the PowerMac12,1
+	  which is the iMac G5 (iSight) 970MP
+
 config ANSLCD
 	tristate "Support for ANS LCD display"
 	depends on ADB_CUDA && PPC_PMAC
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index 2dfc3f4..e3132ef 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -42,4 +42,9 @@ obj-$(CONFIG_WINDFARM_PM112)	+= windfarm_pm112.o windfarm_smu_sat.o \
 				   windfarm_smu_sensors.o \
 				   windfarm_max6690_sensor.o \
 				   windfarm_lm75_sensor.o windfarm_pid.o
+obj-$(CONFIG_WINDFARM_PM121)	+= windfarm_pm121.o windfarm_smu_sat.o \
+				   windfarm_smu_controls.o \
+				   windfarm_smu_sensors.o \
+				   windfarm_max6690_sensor.o \
+				   windfarm_lm75_sensor.o windfarm_pid.o
 obj-$(CONFIG_PMAC_RACKMETER)	+= rack-meter.o
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index 7e10c3a..b92b959 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -127,6 +127,12 @@ static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
 	 */
 	if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY"))
 		lm->sens.name = "hd-temp";
+	else if (!strcmp(loc, "Incoming Air Temp"))
+		lm->sens.name = "incoming-air-temp";
+	else if (!strcmp(loc, "ODD Temp"))
+		lm->sens.name = "optical-drive-temp";
+	else if (!strcmp(loc, "HD Temp"))
+		lm->sens.name = "hard-drive-temp";
 	else
 		goto fail;
 
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c
index 5f03aab..e207a90 100644
--- a/drivers/macintosh/windfarm_max6690_sensor.c
+++ b/drivers/macintosh/windfarm_max6690_sensor.c
@@ -77,18 +77,28 @@ static struct wf_sensor_ops wf_max6690_ops = {
 	.owner		= THIS_MODULE,
 };
 
-static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr)
+static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr,
+			      const char *loc)
 {
 	struct wf_6690_sensor *max;
-	char *name = "backside-temp";
+	char *name;
 
 	max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
 	if (max == NULL) {
 		printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: "
-		       "no memory\n", name);
+		       "no memory\n", loc);
 		return;
 	}
 
+	if (!strcmp(loc, "BACKSIDE"))
+		name = "backside-temp";
+	else if (!strcmp(loc, "NB Ambient"))
+		name = "north-bridge-temp";
+	else if (!strcmp(loc, "GPU Ambient"))
+		name = "gpu-temp";
+	else
+		goto fail;
+
 	max->sens.ops = &wf_max6690_ops;
 	max->sens.name = name;
 	max->i2c.addr = addr >> 1;
@@ -138,9 +148,7 @@ static int wf_max6690_attach(struct i2c_adapter *adapter)
 		if (loc == NULL || addr == 0)
 			continue;
 		printk("found max6690, loc=%s addr=0x%02x\n", loc, addr);
-		if (strcmp(loc, "BACKSIDE"))
-			continue;
-		wf_max6690_create(adapter, addr);
+		wf_max6690_create(adapter, addr, loc);
 	}
 
 	return 0;
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
new file mode 100644
index 0000000..6898a09
--- /dev/null
+++ b/drivers/macintosh/windfarm_pm121.c
@@ -0,0 +1,1041 @@
+/*
+ * Windfarm PowerMac thermal control. iMac G5 iSight
+ *
+ * (c) Copyright 2007 Étienne Bersac <bersace@gmail.com>
+ *
+ * Bits & pieces from windfarm_pm81.c by (c) Copyright 2005 Benjamin
+ * Herrenschmidt, IBM Corp. <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ *
+ *
+ * PowerMac12,1
+ * ============
+ *
+ *
+ * The algorithm used is the PID control algorithm, used the same way
+ * the published Darwin code does, using the same values that are
+ * present in the Darwin 8.10 snapshot property lists (note however
+ * that none of the code has been re-used, it's a complete
+ * re-implementation
+ *
+ * There is two models using PowerMac12,1. Model 2 is iMac G5 iSight
+ * 17" while Model 3 is iMac G5 20". They do have both the same
+ * controls with a tiny difference. The control-ids of hard-drive-fan
+ * and cpu-fan is swapped.
+ *
+ *
+ * Target Correction :
+ *
+ * controls have a target correction calculated as :
+ *
+ * new_min = ((((average_power * slope) >> 16) + offset) >> 16) + min_value
+ * new_value = max(new_value, max(new_min, 0))
+ *
+ * OD Fan control correction.
+ *
+ * # model_id: 2
+ *   offset		: -19563152
+ *   slope		:  1956315
+ *
+ * # model_id: 3
+ *   offset		: -15650652
+ *   slope		:  1565065
+ *
+ * HD Fan control correction.
+ *
+ * # model_id: 2
+ *   offset		: -15650652
+ *   slope		:  1565065
+ *
+ * # model_id: 3
+ *   offset		: -19563152
+ *   slope		:  1956315
+ *
+ * CPU Fan control correction.
+ *
+ * # model_id: 2
+ *   offset		: -25431900
+ *   slope		:  2543190
+ *
+ * # model_id: 3
+ *   offset		: -15650652
+ *   slope		:  1565065
+ *
+ *
+ * Target rubber-banding :
+ *
+ * Some controls have a target correction which depends on another
+ * control value. The correction is computed in the following way :
+ *
+ * new_min = ref_value * slope + offset
+ *
+ * ref_value is the value of the reference control. If new_min is
+ * greater than 0, then we correct the target value using :
+ *
+ * new_target = max (new_target, new_min >> 16)
+ *
+ *
+ * # model_id : 2
+ *   control	: cpu-fan
+ *   ref	: optical-drive-fan
+ *   offset	: -15650652
+ *   slope	: 1565065
+ *
+ * # model_id : 3
+ *   control	: optical-drive-fan
+ *   ref	: hard-drive-fan
+ *   offset	: -32768000
+ *   slope	: 65536
+ *
+ *
+ * In order to have the moste efficient correction with those
+ * dependencies, we must trigger HD loop before OD loop before CPU
+ * loop.
+ *
+ *
+ * The various control loops found in Darwin config file are:
+ *
+ * HD Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : hard-drive-fan
+ *   sensor         : hard-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x002D70A3
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x370000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : hard-drive-fan
+ *   sensor         : hard-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x002170A3
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x370000
+ *                    Interval = 5s
+ *
+ * OD Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : optical-drive-fan
+ *   sensor         : optical-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x001FAE14
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x320000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : optical-drive-fan
+ *   sensor         : optical-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x001FAE14
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x320000
+ *                    Interval = 5s
+ *
+ * GPU Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : hard-drive-fan
+ *   sensor         : gpu-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x002A6666
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x5A0000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : cpu-fan
+ *   sensor         : gpu-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x0010CCCC
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x500000
+ *                    Interval = 5s
+ *
+ * KODIAK (aka northbridge) Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : optical-drive-fan
+ *   sensor         : north-bridge-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x003BD70A
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x550000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : hard-drive-fan
+ *   sensor         : north-bridge-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x0030F5C2
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x550000
+ *                    Interval = 5s
+ *
+ * CPU Fan control loop.
+ *
+ *   control        : cpu-fan
+ *   sensors        : cpu-temp, cpu-power
+ *   PID params     : from SDB partition
+ *
+ *
+ * CPU Slew control loop.
+ *
+ *   control        : cpufreq-clamp
+ *   sensor         : cpu-temp
+ *
+ */
+
+#undef	DEBUG
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/kmod.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+
+#define VERSION "0.3"
+
+static int pm121_mach_model;	/* machine model id */
+
+/* Controls & sensors */
+static struct wf_sensor	*sensor_cpu_power;
+static struct wf_sensor	*sensor_cpu_temp;
+static struct wf_sensor	*sensor_cpu_voltage;
+static struct wf_sensor	*sensor_cpu_current;
+static struct wf_sensor	*sensor_gpu_temp;
+static struct wf_sensor	*sensor_north_bridge_temp;
+static struct wf_sensor	*sensor_hard_drive_temp;
+static struct wf_sensor	*sensor_optical_drive_temp;
+static struct wf_sensor	*sensor_incoming_air_temp; /* unused ! */
+
+enum {
+	FAN_CPU,
+	FAN_HD,
+	FAN_OD,
+	CPUFREQ,
+	N_CONTROLS
+};
+static struct wf_control *controls[N_CONTROLS] = {};
+
+/* Set to kick the control loop into life */
+static int pm121_all_controls_ok, pm121_all_sensors_ok, pm121_started;
+
+enum {
+	FAILURE_FAN		= 1 << 0,
+	FAILURE_SENSOR		= 1 << 1,
+	FAILURE_OVERTEMP	= 1 << 2
+};
+
+/* All sys loops. Note the HD before the OD loop in order to have it
+   run before. */
+enum {
+	LOOP_GPU,		/* control = hd or cpu, but luckily,
+				   it doesn't matter */
+	LOOP_HD,		/* control = hd */
+	LOOP_KODIAK,		/* control = hd or od */
+	LOOP_OD,		/* control = od */
+	N_LOOPS
+};
+
+static const char *loop_names[N_LOOPS] = {
+	"GPU",
+	"HD",
+	"KODIAK",
+	"OD",
+};
+
+#define	PM121_NUM_CONFIGS	2
+
+static unsigned int pm121_failure_state;
+static int pm121_readjust, pm121_skipping;
+static s32 average_power;
+
+struct pm121_correction {
+	int	offset;
+	int	slope;
+};
+
+static const struct pm121_correction
+corrections[N_CONTROLS][PM121_NUM_CONFIGS] = {
+	/* FAN_OD */
+	{
+		/* MODEL 2 */
+		{ .offset	= -19563152,
+		  .slope	=  1956315
+		},
+		/* MODEL 3 */
+		{ .offset	= -15650652,
+		  .slope	=  1565065
+		},
+	},
+	/* FAN_HD */
+	{
+		/* MODEL 2 */
+		{ .offset	= -15650652,
+		  .slope	=  1565065
+		},
+		/* MODEL 3 */
+		{ .offset	= -19563152,
+		  .slope	=  1956315
+		},
+	},
+	/* FAN_CPU */
+	{
+		/* MODEL 2 */
+		{ .offset	= -25431900,
+		  .slope	=  2543190
+		},
+		/* MODEL 3 */
+		{ .offset	= -15650652,
+		  .slope	=  1565065
+		},
+	},
+	/* CPUFREQ has no correction (and is not implemented at all) */
+};
+
+struct pm121_connection {
+	unsigned int	control_id;
+	unsigned int	ref_id;
+	struct pm121_correction	correction;
+};
+
+static const struct pm121_connection pm121_connections[] = {
+	/* MODEL 2 */
+	{ .control_id	= FAN_CPU,
+	  .ref_id	= FAN_OD,
+	  { .offset	= -32768000,
+	    .slope	=  65536
+	  }
+	},
+	/* MODEL 3 */
+	{ .control_id	= FAN_OD,
+	  .ref_id	= FAN_HD,
+	  { .offset	= -32768000,
+	    .slope	=  65536
+	  }
+	},
+};
+
+/* pointer to the current model connection */
+static const struct pm121_connection *pm121_connection;
+
+/*
+ * ****** System Fans Control Loop ******
+ *
+ */
+
+/* Since each loop handles only one control and we want to avoid
+ * writing virtual control, we store the control correction with the
+ * loop params. Some data are not set, there are common to all loop
+ * and thus, hardcoded.
+ */
+struct pm121_sys_param {
+	/* purely informative since we use mach_model-2 as index */
+	int			model_id;
+	struct wf_sensor	**sensor; /* use sensor_id instead ? */
+	s32			gp, itarget;
+	unsigned int		control_id;
+};
+
+static const struct pm121_sys_param
+pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] = {
+	/* GPU Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_gpu_temp,
+		  .gp		= 0x002A6666,
+		  .itarget	= 0x5A0000,
+		  .control_id	= FAN_HD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_gpu_temp,
+		  .gp		= 0x0010CCCC,
+		  .itarget	= 0x500000,
+		  .control_id	= FAN_CPU,
+		},
+	},
+	/* HD Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_hard_drive_temp,
+		  .gp		= 0x002D70A3,
+		  .itarget	= 0x370000,
+		  .control_id	= FAN_HD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_hard_drive_temp,
+		  .gp		= 0x002170A3,
+		  .itarget	= 0x370000,
+		  .control_id	= FAN_HD,
+		},
+	},
+	/* KODIAK Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_north_bridge_temp,
+		  .gp		= 0x003BD70A,
+		  .itarget	= 0x550000,
+		  .control_id	= FAN_OD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_north_bridge_temp,
+		  .gp		= 0x0030F5C2,
+		  .itarget	= 0x550000,
+		  .control_id	= FAN_HD,
+		},
+	},
+	/* OD Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_optical_drive_temp,
+		  .gp		= 0x001FAE14,
+		  .itarget	= 0x320000,
+		  .control_id	= FAN_OD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_optical_drive_temp,
+		  .gp		= 0x001FAE14,
+		  .itarget	= 0x320000,
+		  .control_id	= FAN_OD,
+		},
+	},
+};
+
+/* the hardcoded values */
+#define	PM121_SYS_GD		0x00000000
+#define	PM121_SYS_GR		0x00019999
+#define	PM121_SYS_HISTORY_SIZE	2
+#define	PM121_SYS_INTERVAL	5
+
+/* State data used by the system fans control loop
+ */
+struct pm121_sys_state {
+	int			ticks;
+	s32			setpoint;
+	struct wf_pid_state	pid;
+};
+
+struct pm121_sys_state *pm121_sys_state[N_LOOPS] = {};
+
+/*
+ * ****** CPU Fans Control Loop ******
+ *
+ */
+
+#define PM121_CPU_INTERVAL	1
+
+/* State data used by the cpu fans control loop
+ */
+struct pm121_cpu_state {
+	int			ticks;
+	s32			setpoint;
+	struct wf_cpu_pid_state	pid;
+};
+
+static struct pm121_cpu_state *pm121_cpu_state;
+
+
+
+/*
+ * ***** Implementation *****
+ *
+ */
+
+/* correction the value using the output-low-bound correction algo */
+static s32 pm121_correct(s32 new_setpoint,
+			 unsigned int control_id,
+			 s32 min)
+{
+	s32 new_min;
+	struct pm121_correction *correction;
+	correction = &corrections[control_id][pm121_mach_model - 2];
+
+	new_min = (average_power * correction->slope) >> 16;
+	new_min += correction->offset;
+	new_min = (new_min >> 16) + min;
+
+	return max(new_setpoint, max(new_min, 0));
+}
+
+static s32 pm121_connect(unsigned int control_id, s32 setpoint)
+{
+	s32 new_min, value, new_setpoint;
+
+	if (pm121_connection->control_id == control_id) {
+		controls[control_id]->ops->get_value(controls[control_id],
+						     &value);
+		new_min = value * pm121_connection->correction.slope;
+		new_min += pm121_connection->correction.offset;
+		if (new_min > 0) {
+			new_setpoint = max(setpoint, (new_min >> 16));
+			if (new_setpoint != setpoint) {
+				pr_debug("pm121: %s depending on %s, "
+					 "corrected from %d to %d RPM\n",
+					 controls[control_id]->name,
+					 controls[pm121_connection->ref_id]->name,
+					 (int) setpoint, (int) new_setpoint);
+			}
+		} else
+			new_setpoint = setpoint;
+	}
+	/* no connection */
+	else
+		new_setpoint = setpoint;
+
+	return new_setpoint;
+}
+
+/* FAN LOOPS */
+static void pm121_create_sys_fans(int loop_id)
+{
+	struct pm121_sys_param *param = NULL;
+	struct wf_pid_param pid_param;
+	struct wf_control *control = NULL;
+	int i;
+
+	/* First, locate the params for this model */
+	for (i = 0; i < PM121_NUM_CONFIGS; i++) {
+		if (pm121_sys_all_params[loop_id][i].model_id == pm121_mach_model) {
+			param = &(pm121_sys_all_params[loop_id][i]);
+			break;
+		}
+	}
+
+	/* No params found, put fans to max */
+	if (param == NULL) {
+		printk(KERN_WARNING "pm121: %s fan config not found "
+		       " for this machine model\n",
+		       loop_names[loop_id]);
+		goto fail;
+	}
+
+	control = controls[param->control_id];
+
+	/* Alloc & initialize state */
+	pm121_sys_state[loop_id] = kmalloc(sizeof(struct pm121_sys_state),
+					   GFP_KERNEL);
+	if (pm121_sys_state[loop_id] == NULL) {
+		printk(KERN_WARNING "pm121: Memory allocation error\n");
+		goto fail;
+	}
+	pm121_sys_state[loop_id]->ticks = 1;
+
+	/* Fill PID params */
+	pid_param.gd		= PM121_SYS_GD;
+	pid_param.gp		= param->gp;
+	pid_param.gr		= PM121_SYS_GR;
+	pid_param.interval	= PM121_SYS_INTERVAL;
+	pid_param.history_len	= PM121_SYS_HISTORY_SIZE;
+	pid_param.itarget	= param->itarget;
+	pid_param.min		= control->ops->get_min(control);
+	pid_param.max		= control->ops->get_max(control);
+
+	wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param);
+
+	pr_debug("pm121: %s Fan control loop initialized.\n"
+		 "       itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
+		 loop_names[loop_id], FIX32TOPRINT(pid_param.itarget),
+		 pid_param.min, pid_param.max);
+	return;
+
+ fail:
+	/* note that this is not optimal since another loop may still
+	   control the same control */
+	printk(KERN_WARNING "pm121: failed to set up %s loop "
+	       "setting \"%s\" to max speed.\n",
+	       loop_names[loop_id], control->name);
+
+	if (control)
+		wf_control_set_max(control);
+}
+
+static void pm121_sys_fans_tick(int loop_id)
+{
+	struct pm121_sys_param *param;
+	struct pm121_sys_state *st;
+	struct wf_sensor *sensor;
+	struct wf_control *control;
+	s32 temp, new_setpoint;
+	int rc;
+
+	param = &(pm121_sys_all_params[loop_id][pm121_mach_model-2]);
+	st = pm121_sys_state[loop_id];
+	sensor = *(param->sensor);
+	control = controls[param->control_id];
+
+	if (--st->ticks != 0) {
+		if (pm121_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = PM121_SYS_INTERVAL;
+
+	rc = sensor->ops->get_value(sensor, &temp);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: %s sensor error %d\n",
+		       sensor->name, rc);
+		pm121_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	pr_debug("pm121: %s Fan tick ! %s: %d.%03d\n",
+		 loop_names[loop_id], sensor->name,
+		 FIX32TOPRINT(temp));
+
+	new_setpoint = wf_pid_run(&st->pid, temp);
+
+	/* correction */
+	new_setpoint = pm121_correct(new_setpoint,
+				     param->control_id,
+				     st->pid.param.min);
+	/* linked corretion */
+	new_setpoint = pm121_connect(param->control_id, new_setpoint);
+
+	if (new_setpoint == st->setpoint)
+		return;
+	st->setpoint = new_setpoint;
+	pr_debug("pm121: %s corrected setpoint: %d RPM\n",
+		 control->name, (int)new_setpoint);
+ readjust:
+	if (control && pm121_failure_state == 0) {
+		rc = control->ops->set_value(control, st->setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: %s fan error %d\n",
+			       control->name, rc);
+			pm121_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+
+/* CPU LOOP */
+static void pm121_create_cpu_fans(void)
+{
+	struct wf_cpu_pid_param pid_param;
+	const struct smu_sdbp_header *hdr;
+	struct smu_sdbp_cpupiddata *piddata;
+	struct smu_sdbp_fvt *fvt;
+	struct wf_control *fan_cpu;
+	s32 tmax, tdelta, maxpow, powadj;
+
+	fan_cpu = controls[FAN_CPU];
+
+	/* First, locate the PID params in SMU SBD */
+	hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
+	if (hdr == 0) {
+		printk(KERN_WARNING "pm121: CPU PID fan config not found.\n");
+		goto fail;
+	}
+	piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
+
+	/* Get the FVT params for operating point 0 (the only supported one
+	 * for now) in order to get tmax
+	 */
+	hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
+	if (hdr) {
+		fvt = (struct smu_sdbp_fvt *)&hdr[1];
+		tmax = ((s32)fvt->maxtemp) << 16;
+	} else
+		tmax = 0x5e0000; /* 94 degree default */
+
+	/* Alloc & initialize state */
+	pm121_cpu_state = kmalloc(sizeof(struct pm121_cpu_state),
+				  GFP_KERNEL);
+	if (pm121_cpu_state == NULL)
+		goto fail;
+	pm121_cpu_state->ticks = 1;
+
+	/* Fill PID params */
+	pid_param.interval = PM121_CPU_INTERVAL;
+	pid_param.history_len = piddata->history_len;
+	if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
+		printk(KERN_WARNING "pm121: History size overflow on "
+		       "CPU control loop (%d)\n", piddata->history_len);
+		pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
+	}
+	pid_param.gd = piddata->gd;
+	pid_param.gp = piddata->gp;
+	pid_param.gr = piddata->gr / pid_param.history_len;
+
+	tdelta = ((s32)piddata->target_temp_delta) << 16;
+	maxpow = ((s32)piddata->max_power) << 16;
+	powadj = ((s32)piddata->power_adj) << 16;
+
+	pid_param.tmax = tmax;
+	pid_param.ttarget = tmax - tdelta;
+	pid_param.pmaxadj = maxpow - powadj;
+
+	pid_param.min = fan_cpu->ops->get_min(fan_cpu);
+	pid_param.max = fan_cpu->ops->get_max(fan_cpu);
+
+	wf_cpu_pid_init(&pm121_cpu_state->pid, &pid_param);
+
+	pr_debug("pm121: CPU Fan control initialized.\n");
+	pr_debug("       ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM,\n",
+		 FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
+		 pid_param.min, pid_param.max);
+
+	return;
+
+ fail:
+	printk(KERN_WARNING "pm121: CPU fan config not found, max fan speed\n");
+
+	if (controls[CPUFREQ])
+		wf_control_set_max(controls[CPUFREQ]);
+	if (fan_cpu)
+		wf_control_set_max(fan_cpu);
+}
+
+
+static void pm121_cpu_fans_tick(struct pm121_cpu_state *st)
+{
+	s32 new_setpoint, temp, power;
+	struct wf_control *fan_cpu = NULL;
+	int rc;
+
+	if (--st->ticks != 0) {
+		if (pm121_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = PM121_CPU_INTERVAL;
+
+	fan_cpu = controls[FAN_CPU];
+
+	rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+	if (rc) {
+		printk(KERN_WARNING "pm121: CPU temp sensor error %d\n",
+		       rc);
+		pm121_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+	if (rc) {
+		printk(KERN_WARNING "pm121: CPU power sensor error %d\n",
+		       rc);
+		pm121_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	pr_debug("pm121: CPU Fans tick ! CPU temp: %d.%03d°C, power: %d.%03d\n",
+		 FIX32TOPRINT(temp), FIX32TOPRINT(power));
+
+	if (temp > st->pid.param.tmax)
+		pm121_failure_state |= FAILURE_OVERTEMP;
+
+	new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
+
+	/* correction */
+	new_setpoint = pm121_correct(new_setpoint,
+				     FAN_CPU,
+				     st->pid.param.min);
+
+	/* connected correction */
+	new_setpoint = pm121_connect(FAN_CPU, new_setpoint);
+
+	if (st->setpoint == new_setpoint)
+		return;
+	st->setpoint = new_setpoint;
+	pr_debug("pm121: CPU corrected setpoint: %d RPM\n", (int)new_setpoint);
+
+ readjust:
+	if (fan_cpu && pm121_failure_state == 0) {
+		rc = fan_cpu->ops->set_value(fan_cpu, st->setpoint);
+		if (rc) {
+			printk(KERN_WARNING "pm121: %s fan error %d\n",
+			       fan_cpu->name, rc);
+			pm121_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+/*
+ * ****** Common ******
+ *
+ */
+
+static void pm121_tick(void)
+{
+	unsigned int last_failure = pm121_failure_state;
+	unsigned int new_failure;
+	s32 total_power;
+	int i;
+
+	if (!pm121_started) {
+		pr_debug("pm121: creating control loops !\n");
+		for (i = 0; i < N_LOOPS; i++)
+			pm121_create_sys_fans(i);
+
+		pm121_create_cpu_fans();
+		pm121_started = 1;
+	}
+
+	/* skipping ticks */
+	if (pm121_skipping && --pm121_skipping)
+		return;
+
+	/* compute average power */
+	total_power = 0;
+	for (i = 0; i < pm121_cpu_state->pid.param.history_len; i++)
+		total_power += pm121_cpu_state->pid.powers[i];
+
+	average_power = total_power / pm121_cpu_state->pid.param.history_len;
+
+
+	pm121_failure_state = 0;
+	for (i = 0 ; i < N_LOOPS; i++) {
+		if (pm121_sys_state[i])
+			pm121_sys_fans_tick(i);
+	}
+
+	if (pm121_cpu_state)
+		pm121_cpu_fans_tick(pm121_cpu_state);
+
+	pm121_readjust = 0;
+	new_failure = pm121_failure_state & ~last_failure;
+
+	/* If entering failure mode, clamp cpufreq and ramp all
+	 * fans to full speed.
+	 */
+	if (pm121_failure_state && !last_failure) {
+		for (i = 0; i < N_CONTROLS; i++) {
+			if (controls[i])
+				wf_control_set_max(controls[i]);
+		}
+	}
+
+	/* If leaving failure mode, unclamp cpufreq and readjust
+	 * all fans on next iteration
+	 */
+	if (!pm121_failure_state && last_failure) {
+		if (controls[CPUFREQ])
+			wf_control_set_min(controls[CPUFREQ]);
+		pm121_readjust = 1;
+	}
+
+	/* Overtemp condition detected, notify and start skipping a couple
+	 * ticks to let the temperature go down
+	 */
+	if (new_failure & FAILURE_OVERTEMP) {
+		wf_set_overtemp();
+		pm121_skipping = 2;
+	}
+
+	/* We only clear the overtemp condition if overtemp is cleared
+	 * _and_ no other failure is present. Since a sensor error will
+	 * clear the overtemp condition (can't measure temperature) at
+	 * the control loop levels, but we don't want to keep it clear
+	 * here in this case
+	 */
+	if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+		wf_clear_overtemp();
+}
+
+
+static struct wf_control* pm121_register_control(struct wf_control *ct,
+						 const char *match,
+						 unsigned int id)
+{
+	if (controls[id] == NULL && !strcmp(ct->name, match)) {
+		if (wf_get_control(ct) == 0)
+			controls[FAN_OD] = ct;
+	}
+	return controls[id];
+}
+
+static void pm121_new_control(struct wf_control *ct)
+{
+	int all = 1;
+
+	if (pm121_all_controls_ok)
+		return;
+
+	all = all && pm121_register_control(ct, "optical-driver-fan", FAN_OD);
+	all = all && pm121_register_control(ct, "hard-driver-fan", FAN_HD);
+	all = all && pm121_register_control(ct, "cpu-driver-fan", FAN_CPU);
+	all = all && pm121_register_control(ct, "cpufreq-clamp", CPUFREQ);
+
+	if (all)
+		pm121_all_controls_ok = 1;
+}
+
+
+
+
+static struct wf_sensor* pm121_register_sensor(struct wf_sensor *sensor,
+					       const char *match,
+					       struct wf_sensor **var)
+{
+	if (*var == NULL && !strcmp(sensor->name, match)) {
+		if (wf_get_sensor(sensor) == 0)
+			*var = sensor;
+	}
+	return *var;
+}
+
+static void pm121_new_sensor(struct wf_sensor *sr)
+{
+	int all = 1;
+
+	if (pm121_all_sensors_ok)
+		return;
+
+	all = all && pm121_register_sensor(sr, "cpu-temp",
+					   &sensor_cpu_temp);
+	all = all && pm121_register_sensor(sr, "cpu-current",
+					   &sensor_cpu_current);
+	all = all && pm121_register_sensor(sr, "cpu-voltage",
+					   &sensor_cpu_voltage);
+	all = all && pm121_register_sensor(sr, "cpu-power",
+					   &sensor_cpu_power);
+	all = all && pm121_register_sensor(sr, "hard-drive-temp",
+					   &sensor_hard_drive_temp);
+	all = all && pm121_register_sensor(sr, "optical-drive-temp",
+					   &sensor_optical_drive_temp);
+	all = all && pm121_register_sensor(sr, "incoming-air-temp",
+					   &sensor_incoming_air_temp);
+	all = all && pm121_register_sensor(sr, "north-bridge-temp",
+					   &sensor_north_bridge_temp);
+	all = all && pm121_register_sensor(sr, "gpu-temp",
+					   &sensor_gpu_temp);
+
+	if (all)
+		pm121_all_sensors_ok = 1;
+}
+
+
+
+static int pm121_notify(struct notifier_block *self,
+			unsigned long event, void *data)
+{
+	switch (event) {
+	case WF_EVENT_NEW_CONTROL:
+		pr_debug("pm121: new control %s detected\n",
+			 ((struct wf_control *)data)->name);
+		pm121_new_control(data);
+		break;
+	case WF_EVENT_NEW_SENSOR:
+		pr_debug("pm121: new sensor %s detected\n",
+			 ((struct wf_sensor *)data)->name);
+		pm121_new_sensor(data);
+		break;
+	case WF_EVENT_TICK:
+		if (pm121_all_controls_ok && pm121_all_sensors_ok)
+			pm121_tick();
+		break;
+	}
+
+	return 0;
+}
+
+static struct notifier_block pm121_events = {
+	.notifier_call	= pm121_notify,
+};
+
+static int pm121_init_pm(void)
+{
+	const struct smu_sdbp_header *hdr;
+
+	hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
+	if (hdr != 0) {
+		struct smu_sdbp_sensortree *st =
+			(struct smu_sdbp_sensortree *)&hdr[1];
+		pm121_mach_model = st->model_id;
+	}
+
+	pm121_connection = &pm121_connections[pm121_mach_model - 2];
+
+	printk(KERN_INFO "pm121: Initializing for iMac G5 iSight model ID %d\n",
+	       pm121_mach_model);
+
+	return 0;
+}
+
+
+static int pm121_probe(struct platform_device *ddev)
+{
+	wf_register_client(&pm121_events);
+
+	return 0;
+}
+
+static int __devexit pm121_remove(struct platform_device *ddev)
+{
+	wf_unregister_client(&pm121_events);
+	return 0;
+}
+
+static struct platform_driver pm121_driver = {
+	.probe = pm121_probe,
+	.remove = __devexit_p(pm121_remove),
+	.driver = {
+		.name = "windfarm",
+		.bus = &platform_bus_type,
+	},
+};
+
+
+static int __init pm121_init(void)
+{
+	int rc = -ENODEV;
+
+	if (machine_is_compatible("PowerMac12,1"))
+		rc = pm121_init_pm();
+
+	if (rc == 0) {
+		request_module("windfarm_smu_controls");
+		request_module("windfarm_smu_sensors");
+		request_module("windfarm_smu_sat");
+		request_module("windfarm_lm75_sensor");
+		request_module("windfarm_max6690_sensor");
+		request_module("windfarm_cpufreq_clamp");
+		platform_driver_register(&pm121_driver);
+	}
+
+	return rc;
+}
+
+static void __exit pm121_exit(void)
+{
+
+	platform_driver_unregister(&pm121_driver);
+}
+
+
+module_init(pm121_init);
+module_exit(pm121_exit);
+
+MODULE_AUTHOR("Étienne Bersac <bersace@gmail.com>");
+MODULE_DESCRIPTION("Thermal control logic for iMac G5 (iSight)");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index 58c2590..961fa0e 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -218,6 +218,10 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
 		fct->ctrl.name = "cpu-fan";
 	else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
 		fct->ctrl.name = "drive-bay-fan";
+	else if (!strcmp(l, "HDD Fan")) /* seen on iMac G5 iSight */
+		fct->ctrl.name = "hard-drive-fan";
+	else if (!strcmp(l, "ODD Fan")) /* same */
+		fct->ctrl.name = "optical-drive-fan";
 
 	/* Unrecognized fan, bail out */
 	if (fct->ctrl.name == NULL)

^ permalink raw reply related

* Re: [PATCH 1/5] PowerPC 74xx: Katana Qp device tree
From: Mark A. Greer @ 2007-12-10 17:17 UTC (permalink / raw)
  To: Mark A. Greer, David Gibson, Andrei Dolnikov, linuxppc-dev
In-Reply-To: <20071208013309.GB8662@localhost.localdomain>

On Sat, Dec 08, 2007 at 12:33:09PM +1100, David Gibson wrote:
> On Thu, Dec 06, 2007 at 04:27:56PM -0700, Mark A. Greer wrote:
> > David, et. al.,
> > 
> > This is a big blob patch of what I've changed for the prpmc2800.  It
> > includes the necessary changes in the kernel which you can probably
> > ignore but they're there for reference.  If you like the dts, then I'll
> > split the blob up into logical pieces and Andrei can make similar
> > changes for the Katana Qp.
> > 
> > Let me know what you think.
> 
> Looks pretty reasonable.  I would have preferred that labels be
> uppercase by convention, to make them easier to pick out by eyeball,
> but I think that's a lost cause at this stage.

I can do that.  Thanks for the feedback.  I'll break the blob up into
pieces and submit with the labels in uppercase.

Thanks,

Mark

^ permalink raw reply

* [PATCH] mpc8272ads, pq2fads: Update defconfig with CONFIG_FS_ENET_MDIO_FCC
From: Scott Wood @ 2007-12-10 17:10 UTC (permalink / raw)
  To: galak; +Cc: linuxppc-dev

This was recently made configurable, and needs to be set for these boards.

Signed-off-by: Scott Wood <scottwood@freescale.com>
---
Please apply for 2.6.24.

 arch/powerpc/configs/mpc8272_ads_defconfig |  110 +++++++++++---------
 arch/powerpc/configs/pq2fads_defconfig     |  152 +++++++++++++++++-----------
 2 files changed, 153 insertions(+), 109 deletions(-)

diff --git a/arch/powerpc/configs/mpc8272_ads_defconfig b/arch/powerpc/configs/mpc8272_ads_defconfig
index 865a942..e982a29 100644
--- a/arch/powerpc/configs/mpc8272_ads_defconfig
+++ b/arch/powerpc/configs/mpc8272_ads_defconfig
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.23-rc4
-# Wed Sep  5 12:43:23 2007
+# Linux kernel version: 2.6.24-rc3
+# Mon Dec 10 11:02:24 2007
 #
 # CONFIG_PPC64 is not set
 
@@ -20,8 +20,13 @@ CONFIG_PPC_STD_MMU_32=y
 # CONFIG_PPC_MM_SLICES is not set
 # CONFIG_SMP is not set
 CONFIG_PPC32=y
+CONFIG_WORD_SIZE=32
 CONFIG_PPC_MERGE=y
 CONFIG_MMU=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GENERIC_CLOCKEVENTS=y
 CONFIG_GENERIC_HARDIRQS=y
 CONFIG_IRQ_PER_CPU=y
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
@@ -63,6 +68,10 @@ CONFIG_SYSVIPC_SYSCTL=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
 CONFIG_SYSFS_DEPRECATED=y
 # CONFIG_RELAY is not set
 # CONFIG_BLK_DEV_INITRD is not set
@@ -81,7 +90,6 @@ CONFIG_FUTEX=y
 CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
 CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
 CONFIG_EVENTFD=y
 CONFIG_SHMEM=y
 CONFIG_VM_EVENT_COUNTERS=y
@@ -115,7 +123,6 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
 # Platform support
 #
 # CONFIG_PPC_MULTIPLATFORM is not set
-# CONFIG_EMBEDDED6xx is not set
 CONFIG_PPC_82xx=y
 # CONFIG_PPC_83xx is not set
 # CONFIG_PPC_86xx is not set
@@ -124,6 +131,7 @@ CONFIG_PPC_82xx=y
 # CONFIG_PPC_CELL is not set
 # CONFIG_PPC_CELL_NATIVE is not set
 CONFIG_MPC8272_ADS=y
+# CONFIG_PQ2FADS is not set
 CONFIG_PQ2ADS=y
 CONFIG_8260=y
 CONFIG_8272=y
@@ -147,6 +155,10 @@ CONFIG_CPM=y
 # Kernel options
 #
 # CONFIG_HIGHMEM is not set
+# CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
 # CONFIG_HZ_100 is not set
 CONFIG_HZ_250=y
 # CONFIG_HZ_300 is not set
@@ -163,6 +175,7 @@ CONFIG_ARCH_POPULATES_NODE_MAP=y
 CONFIG_FLATMEM=y
 CONFIG_FLAT_NODE_MEM_MAP=y
 # CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
 CONFIG_SPLIT_PTLOCK_CPUS=4
 # CONFIG_RESOURCES_64BIT is not set
 CONFIG_ZONE_DMA_FLAG=1
@@ -171,9 +184,10 @@ CONFIG_VIRT_TO_BUS=y
 CONFIG_PROC_DEVICETREE=y
 # CONFIG_CMDLINE_BOOL is not set
 # CONFIG_PM is not set
+CONFIG_SUSPEND_UP_POSSIBLE=y
+CONFIG_HIBERNATION_UP_POSSIBLE=y
 CONFIG_SECCOMP=y
 CONFIG_WANT_DEVICE_TREE=y
-# CONFIG_BUILD_RAW_IMAGE is not set
 CONFIG_DEVICE_TREE="mpc8272ads.dts"
 CONFIG_ISA_DMA_API=y
 
@@ -190,11 +204,8 @@ CONFIG_PCI_8260=y
 # CONFIG_PCIEPORTBUS is not set
 CONFIG_ARCH_SUPPORTS_MSI=y
 # CONFIG_PCI_MSI is not set
+CONFIG_PCI_LEGACY=y
 # CONFIG_PCI_DEBUG is not set
-
-#
-# PCCARD (PCMCIA/CardBus) support
-#
 # CONFIG_PCCARD is not set
 
 #
@@ -208,7 +219,7 @@ CONFIG_ARCH_SUPPORTS_MSI=y
 CONFIG_HIGHMEM_START=0xfe000000
 CONFIG_LOWMEM_SIZE=0x30000000
 CONFIG_KERNEL_START=0xc0000000
-CONFIG_TASK_SIZE=0x80000000
+CONFIG_TASK_SIZE=0xc0000000
 CONFIG_BOOT_LOAD=0x00400000
 
 #
@@ -245,6 +256,7 @@ CONFIG_INET_TUNNEL=y
 CONFIG_INET_XFRM_MODE_TRANSPORT=y
 CONFIG_INET_XFRM_MODE_TUNNEL=y
 CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
 CONFIG_INET_DIAG=y
 CONFIG_INET_TCP_DIAG=y
 # CONFIG_TCP_CONG_ADVANCED is not set
@@ -288,10 +300,6 @@ CONFIG_NETFILTER=y
 # CONFIG_LLC2 is not set
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# QoS and/or fair queueing
-#
 # CONFIG_NET_SCHED is not set
 
 #
@@ -317,6 +325,7 @@ CONFIG_NETFILTER=y
 #
 # Generic Driver Options
 #
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_STANDALONE=y
 CONFIG_PREVENT_FIRMWARE_BUILD=y
 # CONFIG_FW_LOADER is not set
@@ -340,6 +349,7 @@ CONFIG_MTD_BLOCK=y
 # CONFIG_INFTL is not set
 # CONFIG_RFD_FTL is not set
 # CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -378,6 +388,7 @@ CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_PHYSMAP is not set
 CONFIG_MTD_PHYSMAP_OF=y
 # CONFIG_MTD_SBC8240 is not set
+# CONFIG_MTD_INTEL_VR_NOR is not set
 # CONFIG_MTD_PLATRAM is not set
 
 #
@@ -429,10 +440,6 @@ CONFIG_BLK_DEV_LOOP=y
 # CONFIG_SCSI_NETLINK is not set
 # CONFIG_ATA is not set
 # CONFIG_MD is not set
-
-#
-# Fusion MPT device support
-#
 # CONFIG_FUSION is not set
 
 #
@@ -451,6 +458,7 @@ CONFIG_NETDEVICES=y
 # CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
 CONFIG_TUN=y
+# CONFIG_VETH is not set
 # CONFIG_ARCNET is not set
 CONFIG_PHYLIB=y
 
@@ -476,20 +484,28 @@ CONFIG_MII=y
 # CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_NET_TULIP is not set
 # CONFIG_HP100 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
 # CONFIG_NET_PCI is not set
+# CONFIG_B44 is not set
 CONFIG_FS_ENET=y
 # CONFIG_FS_ENET_HAS_SCC is not set
 CONFIG_FS_ENET_HAS_FCC=y
+CONFIG_FS_ENET_MDIO_FCC=y
 CONFIG_NETDEV_1000=y
 # CONFIG_ACENIC is not set
 # CONFIG_DL2K is not set
 # CONFIG_E1000 is not set
+# CONFIG_E1000E is not set
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_R8169 is not set
 # CONFIG_SIS190 is not set
 # CONFIG_SKGE is not set
 # CONFIG_SKY2 is not set
+# CONFIG_SK98LIN is not set
 # CONFIG_VIA_VELOCITY is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_BNX2 is not set
@@ -497,11 +513,14 @@ CONFIG_NETDEV_1000=y
 CONFIG_NETDEV_10000=y
 # CONFIG_CHELSIO_T1 is not set
 # CONFIG_CHELSIO_T3 is not set
+# CONFIG_IXGBE is not set
 # CONFIG_IXGB is not set
 # CONFIG_S2IO is not set
 # CONFIG_MYRI10GE is not set
 # CONFIG_NETXEN_NIC is not set
+# CONFIG_NIU is not set
 # CONFIG_MLX4_CORE is not set
+# CONFIG_TEHUTI is not set
 # CONFIG_TR is not set
 
 #
@@ -539,7 +558,6 @@ CONFIG_INPUT_MOUSEDEV_PSAUX=y
 CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
 CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
 # CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_TSDEV is not set
 CONFIG_INPUT_EVDEV=y
 # CONFIG_INPUT_EVBUG is not set
 
@@ -609,14 +627,11 @@ CONFIG_UNIX98_PTYS=y
 CONFIG_LEGACY_PTYS=y
 CONFIG_LEGACY_PTY_COUNT=256
 # CONFIG_IPMI_HANDLER is not set
-# CONFIG_WATCHDOG is not set
 CONFIG_HW_RANDOM=y
 # CONFIG_NVRAM is not set
 # CONFIG_GEN_RTC is not set
 # CONFIG_R3964 is not set
 # CONFIG_APPLICOM is not set
-# CONFIG_AGP is not set
-# CONFIG_DRM is not set
 # CONFIG_RAW_DRIVER is not set
 CONFIG_DEVPORT=y
 # CONFIG_I2C is not set
@@ -629,6 +644,13 @@ CONFIG_DEVPORT=y
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
 
 #
 # Multifunction device drivers
@@ -645,16 +667,17 @@ CONFIG_DAB=y
 #
 # Graphics support
 #
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
 # CONFIG_BACKLIGHT_LCD_SUPPORT is not set
 
 #
 # Display device support
 #
 # CONFIG_DISPLAY_SUPPORT is not set
-# CONFIG_VGASTATE is not set
-# CONFIG_VIDEO_OUTPUT_CONTROL is not set
-# CONFIG_FB is not set
-# CONFIG_FB_IBM_GXT4500 is not set
 
 #
 # Sound
@@ -668,19 +691,6 @@ CONFIG_DAB=y
 # CONFIG_RTC_CLASS is not set
 
 #
-# DMA Engine support
-#
-# CONFIG_DMA_ENGINE is not set
-
-#
-# DMA Clients
-#
-
-#
-# DMA Devices
-#
-
-#
 # Userspace I/O
 #
 # CONFIG_UIO is not set
@@ -696,7 +706,6 @@ CONFIG_EXT3_FS_XATTR=y
 # CONFIG_EXT3_FS_POSIX_ACL is not set
 # CONFIG_EXT3_FS_SECURITY is not set
 CONFIG_JBD=y
-# CONFIG_JBD_DEBUG is not set
 CONFIG_FS_MBCACHE=y
 # CONFIG_REISERFS_FS is not set
 # CONFIG_JFS_FS is not set
@@ -736,7 +745,6 @@ CONFIG_SYSFS=y
 CONFIG_TMPFS=y
 # CONFIG_TMPFS_POSIX_ACL is not set
 # CONFIG_HUGETLB_PAGE is not set
-CONFIG_RAMFS=y
 
 #
 # Miscellaneous filesystems
@@ -749,10 +757,7 @@ CONFIG_CRAMFS=y
 # CONFIG_QNX4FS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
-
-#
-# Network File Systems
-#
+CONFIG_NETWORK_FILESYSTEMS=y
 CONFIG_NFS_FS=y
 CONFIG_NFS_V3=y
 CONFIG_NFS_V3_ACL=y
@@ -790,10 +795,6 @@ CONFIG_MSDOS_PARTITION=y
 # CONFIG_KARMA_PARTITION is not set
 # CONFIG_EFI_PARTITION is not set
 # CONFIG_SYSV68_PARTITION is not set
-
-#
-# Native Language Support
-#
 CONFIG_NLS=y
 CONFIG_NLS_DEFAULT="iso8859-1"
 CONFIG_NLS_CODEPAGE_437=y
@@ -852,11 +853,15 @@ CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
+CONFIG_INSTRUMENTATION=y
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
 
 #
 # Kernel hacking
 #
 # CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
 CONFIG_ENABLE_MUST_CHECK=y
 CONFIG_MAGIC_SYSRQ=y
 # CONFIG_UNUSED_SYMBOLS is not set
@@ -880,8 +885,11 @@ CONFIG_DEBUG_BUGVERBOSE=y
 CONFIG_DEBUG_INFO=y
 # CONFIG_DEBUG_VM is not set
 # CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
 CONFIG_FORCED_INLINING=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
 # CONFIG_FAULT_INJECTION is not set
+# CONFIG_SAMPLES is not set
 # CONFIG_DEBUG_STACKOVERFLOW is not set
 # CONFIG_DEBUG_STACK_USAGE is not set
 # CONFIG_DEBUG_PAGEALLOC is not set
@@ -924,8 +932,12 @@ CONFIG_CRYPTO_DES=y
 # CONFIG_CRYPTO_ARC4 is not set
 # CONFIG_CRYPTO_KHAZAD is not set
 # CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_SEED is not set
 # CONFIG_CRYPTO_DEFLATE is not set
 # CONFIG_CRYPTO_MICHAEL_MIC is not set
 # CONFIG_CRYPTO_CRC32C is not set
 # CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_AUTHENC is not set
 # CONFIG_CRYPTO_HW is not set
+# CONFIG_PPC_CLOCK is not set
+CONFIG_PPC_LIB_RHEAP=y
diff --git a/arch/powerpc/configs/pq2fads_defconfig b/arch/powerpc/configs/pq2fads_defconfig
index 9191f55..6b17562 100644
--- a/arch/powerpc/configs/pq2fads_defconfig
+++ b/arch/powerpc/configs/pq2fads_defconfig
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.23-rc4
-# Thu Aug 30 11:58:17 2007
+# Linux kernel version: 2.6.24-rc3
+# Mon Dec 10 11:01:07 2007
 #
 # CONFIG_PPC64 is not set
 
@@ -20,8 +20,13 @@ CONFIG_PPC_STD_MMU_32=y
 # CONFIG_PPC_MM_SLICES is not set
 # CONFIG_SMP is not set
 CONFIG_PPC32=y
+CONFIG_WORD_SIZE=32
 CONFIG_PPC_MERGE=y
 CONFIG_MMU=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GENERIC_CLOCKEVENTS=y
 CONFIG_GENERIC_HARDIRQS=y
 CONFIG_IRQ_PER_CPU=y
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
@@ -63,6 +68,10 @@ CONFIG_SYSVIPC_SYSCTL=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
 # CONFIG_SYSFS_DEPRECATED is not set
 # CONFIG_RELAY is not set
 CONFIG_BLK_DEV_INITRD=y
@@ -82,7 +91,6 @@ CONFIG_FUTEX=y
 CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
 CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
 CONFIG_EVENTFD=y
 CONFIG_SHMEM=y
 CONFIG_VM_EVENT_COUNTERS=y
@@ -116,7 +124,6 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
 # Platform support
 #
 # CONFIG_PPC_MULTIPLATFORM is not set
-# CONFIG_EMBEDDED6xx is not set
 CONFIG_PPC_82xx=y
 # CONFIG_PPC_83xx is not set
 # CONFIG_PPC_86xx is not set
@@ -126,7 +133,6 @@ CONFIG_PPC_82xx=y
 # CONFIG_PPC_CELL_NATIVE is not set
 # CONFIG_MPC8272_ADS is not set
 CONFIG_PQ2FADS=y
-# CONFIG_EP8248E is not set
 CONFIG_PQ2ADS=y
 CONFIG_8260=y
 CONFIG_PQ2_ADS_PCI_PIC=y
@@ -149,6 +155,10 @@ CONFIG_CPM=y
 # Kernel options
 #
 # CONFIG_HIGHMEM is not set
+# CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
 # CONFIG_HZ_100 is not set
 CONFIG_HZ_250=y
 # CONFIG_HZ_300 is not set
@@ -165,6 +175,7 @@ CONFIG_ARCH_POPULATES_NODE_MAP=y
 CONFIG_FLATMEM=y
 CONFIG_FLAT_NODE_MEM_MAP=y
 # CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
 CONFIG_SPLIT_PTLOCK_CPUS=4
 # CONFIG_RESOURCES_64BIT is not set
 CONFIG_ZONE_DMA_FLAG=1
@@ -173,6 +184,8 @@ CONFIG_VIRT_TO_BUS=y
 CONFIG_PROC_DEVICETREE=y
 # CONFIG_CMDLINE_BOOL is not set
 # CONFIG_PM is not set
+CONFIG_SUSPEND_UP_POSSIBLE=y
+CONFIG_HIBERNATION_UP_POSSIBLE=y
 CONFIG_SECCOMP=y
 CONFIG_WANT_DEVICE_TREE=y
 CONFIG_DEVICE_TREE="pq2fads.dts"
@@ -192,11 +205,8 @@ CONFIG_PCI_8260=y
 # CONFIG_PCIEPORTBUS is not set
 CONFIG_ARCH_SUPPORTS_MSI=y
 # CONFIG_PCI_MSI is not set
+CONFIG_PCI_LEGACY=y
 # CONFIG_PCI_DEBUG is not set
-
-#
-# PCCARD (PCMCIA/CardBus) support
-#
 # CONFIG_PCCARD is not set
 
 #
@@ -210,7 +220,7 @@ CONFIG_ARCH_SUPPORTS_MSI=y
 CONFIG_HIGHMEM_START=0xfe000000
 CONFIG_LOWMEM_SIZE=0x30000000
 CONFIG_KERNEL_START=0xc0000000
-CONFIG_TASK_SIZE=0x80000000
+CONFIG_TASK_SIZE=0xc0000000
 CONFIG_BOOT_LOAD=0x00400000
 
 #
@@ -247,6 +257,7 @@ CONFIG_INET_TUNNEL=y
 CONFIG_INET_XFRM_MODE_TRANSPORT=y
 CONFIG_INET_XFRM_MODE_TUNNEL=y
 CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
 CONFIG_INET_DIAG=y
 CONFIG_INET_TCP_DIAG=y
 # CONFIG_TCP_CONG_ADVANCED is not set
@@ -290,10 +301,6 @@ CONFIG_NETFILTER=y
 # CONFIG_LLC2 is not set
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# QoS and/or fair queueing
-#
 # CONFIG_NET_SCHED is not set
 
 #
@@ -319,6 +326,7 @@ CONFIG_NETFILTER=y
 #
 # Generic Driver Options
 #
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_STANDALONE=y
 CONFIG_PREVENT_FIRMWARE_BUILD=y
 # CONFIG_FW_LOADER is not set
@@ -342,6 +350,7 @@ CONFIG_MTD_BLOCK=y
 # CONFIG_INFTL is not set
 # CONFIG_RFD_FTL is not set
 # CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -380,6 +389,7 @@ CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_PHYSMAP is not set
 CONFIG_MTD_PHYSMAP_OF=y
 # CONFIG_MTD_SBC8240 is not set
+# CONFIG_MTD_INTEL_VR_NOR is not set
 # CONFIG_MTD_PLATRAM is not set
 
 #
@@ -442,10 +452,40 @@ CONFIG_IDE_PROC_FS=y
 # IDE chipset support/bugfixes
 #
 # CONFIG_IDE_GENERIC is not set
-# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_PLATFORM is not set
+
+#
+# PCI IDE chipsets support
+#
 # CONFIG_IDEPCI_PCIBUS_ORDER is not set
+# CONFIG_BLK_DEV_GENERIC is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_JMICRON is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_IT8213 is not set
+# CONFIG_BLK_DEV_IT821X is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SL82C105 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_TC86C001 is not set
 # CONFIG_IDE_ARM is not set
 # CONFIG_BLK_DEV_IDEDMA is not set
+CONFIG_IDE_ARCH_OBSOLETE_INIT=y
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -457,10 +497,6 @@ CONFIG_IDE_PROC_FS=y
 # CONFIG_SCSI_NETLINK is not set
 # CONFIG_ATA is not set
 # CONFIG_MD is not set
-
-#
-# Fusion MPT device support
-#
 # CONFIG_FUSION is not set
 
 #
@@ -479,6 +515,7 @@ CONFIG_NETDEVICES=y
 # CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
 CONFIG_TUN=y
+# CONFIG_VETH is not set
 # CONFIG_ARCNET is not set
 CONFIG_PHYLIB=y
 
@@ -504,20 +541,28 @@ CONFIG_MII=y
 # CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_NET_TULIP is not set
 # CONFIG_HP100 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
 # CONFIG_NET_PCI is not set
+# CONFIG_B44 is not set
 CONFIG_FS_ENET=y
 # CONFIG_FS_ENET_HAS_SCC is not set
 CONFIG_FS_ENET_HAS_FCC=y
+CONFIG_FS_ENET_MDIO_FCC=y
 CONFIG_NETDEV_1000=y
 # CONFIG_ACENIC is not set
 # CONFIG_DL2K is not set
 # CONFIG_E1000 is not set
+# CONFIG_E1000E is not set
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_R8169 is not set
 # CONFIG_SIS190 is not set
 # CONFIG_SKGE is not set
 # CONFIG_SKY2 is not set
+# CONFIG_SK98LIN is not set
 # CONFIG_VIA_VELOCITY is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_BNX2 is not set
@@ -525,11 +570,14 @@ CONFIG_NETDEV_1000=y
 CONFIG_NETDEV_10000=y
 # CONFIG_CHELSIO_T1 is not set
 # CONFIG_CHELSIO_T3 is not set
+# CONFIG_IXGBE is not set
 # CONFIG_IXGB is not set
 # CONFIG_S2IO is not set
 # CONFIG_MYRI10GE is not set
 # CONFIG_NETXEN_NIC is not set
+# CONFIG_NIU is not set
 # CONFIG_MLX4_CORE is not set
+# CONFIG_TEHUTI is not set
 # CONFIG_TR is not set
 
 #
@@ -567,7 +615,6 @@ CONFIG_INPUT_MOUSEDEV_PSAUX=y
 CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
 CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
 # CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_TSDEV is not set
 CONFIG_INPUT_EVDEV=y
 # CONFIG_INPUT_EVBUG is not set
 
@@ -638,14 +685,11 @@ CONFIG_UNIX98_PTYS=y
 CONFIG_LEGACY_PTYS=y
 CONFIG_LEGACY_PTY_COUNT=256
 # CONFIG_IPMI_HANDLER is not set
-# CONFIG_WATCHDOG is not set
 CONFIG_HW_RANDOM=y
 # CONFIG_NVRAM is not set
 # CONFIG_GEN_RTC is not set
 # CONFIG_R3964 is not set
 # CONFIG_APPLICOM is not set
-# CONFIG_AGP is not set
-# CONFIG_DRM is not set
 # CONFIG_RAW_DRIVER is not set
 CONFIG_DEVPORT=y
 # CONFIG_I2C is not set
@@ -658,6 +702,13 @@ CONFIG_DEVPORT=y
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
 
 #
 # Multifunction device drivers
@@ -674,16 +725,17 @@ CONFIG_DAB=y
 #
 # Graphics support
 #
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+# CONFIG_FB is not set
 # CONFIG_BACKLIGHT_LCD_SUPPORT is not set
 
 #
 # Display device support
 #
 # CONFIG_DISPLAY_SUPPORT is not set
-# CONFIG_VGASTATE is not set
-CONFIG_VIDEO_OUTPUT_CONTROL=y
-# CONFIG_FB is not set
-# CONFIG_FB_IBM_GXT4500 is not set
 
 #
 # Sound
@@ -707,6 +759,7 @@ CONFIG_USB_GADGET=y
 # CONFIG_USB_GADGET_DEBUG_FILES is not set
 CONFIG_USB_GADGET_SELECTED=y
 # CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
 # CONFIG_USB_GADGET_FSL_USB2 is not set
 # CONFIG_USB_GADGET_NET2280 is not set
 # CONFIG_USB_GADGET_PXA2XX is not set
@@ -731,19 +784,6 @@ CONFIG_USB_ETH=y
 # CONFIG_RTC_CLASS is not set
 
 #
-# DMA Engine support
-#
-# CONFIG_DMA_ENGINE is not set
-
-#
-# DMA Clients
-#
-
-#
-# DMA Devices
-#
-
-#
 # Userspace I/O
 #
 # CONFIG_UIO is not set
@@ -759,7 +799,6 @@ CONFIG_EXT3_FS_XATTR=y
 # CONFIG_EXT3_FS_POSIX_ACL is not set
 # CONFIG_EXT3_FS_SECURITY is not set
 CONFIG_JBD=y
-# CONFIG_JBD_DEBUG is not set
 CONFIG_FS_MBCACHE=y
 # CONFIG_REISERFS_FS is not set
 # CONFIG_JFS_FS is not set
@@ -799,7 +838,6 @@ CONFIG_SYSFS=y
 CONFIG_TMPFS=y
 # CONFIG_TMPFS_POSIX_ACL is not set
 # CONFIG_HUGETLB_PAGE is not set
-CONFIG_RAMFS=y
 
 #
 # Miscellaneous filesystems
@@ -812,10 +850,7 @@ CONFIG_CRAMFS=y
 # CONFIG_QNX4FS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
-
-#
-# Network File Systems
-#
+CONFIG_NETWORK_FILESYSTEMS=y
 CONFIG_NFS_FS=y
 CONFIG_NFS_V3=y
 CONFIG_NFS_V3_ACL=y
@@ -853,10 +888,6 @@ CONFIG_MSDOS_PARTITION=y
 # CONFIG_KARMA_PARTITION is not set
 # CONFIG_EFI_PARTITION is not set
 # CONFIG_SYSV68_PARTITION is not set
-
-#
-# Native Language Support
-#
 CONFIG_NLS=y
 CONFIG_NLS_DEFAULT="iso8859-1"
 CONFIG_NLS_CODEPAGE_437=y
@@ -915,11 +946,15 @@ CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
+CONFIG_INSTRUMENTATION=y
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
 
 #
 # Kernel hacking
 #
 # CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
 CONFIG_ENABLE_MUST_CHECK=y
 CONFIG_MAGIC_SYSRQ=y
 # CONFIG_UNUSED_SYMBOLS is not set
@@ -943,8 +978,11 @@ CONFIG_DEBUG_BUGVERBOSE=y
 CONFIG_DEBUG_INFO=y
 # CONFIG_DEBUG_VM is not set
 # CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
 CONFIG_FORCED_INLINING=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
 # CONFIG_FAULT_INJECTION is not set
+# CONFIG_SAMPLES is not set
 # CONFIG_DEBUG_STACKOVERFLOW is not set
 # CONFIG_DEBUG_STACK_USAGE is not set
 # CONFIG_DEBUG_PAGEALLOC is not set
@@ -952,16 +990,6 @@ CONFIG_FORCED_INLINING=y
 # CONFIG_KGDB_CONSOLE is not set
 CONFIG_BDI_SWITCH=y
 # CONFIG_PPC_EARLY_DEBUG is not set
-# CONFIG_PPC_EARLY_DEBUG_LPAR is not set
-# CONFIG_PPC_EARLY_DEBUG_G5 is not set
-# CONFIG_PPC_EARLY_DEBUG_RTAS_PANEL is not set
-# CONFIG_PPC_EARLY_DEBUG_RTAS_CONSOLE is not set
-# CONFIG_PPC_EARLY_DEBUG_MAPLE is not set
-# CONFIG_PPC_EARLY_DEBUG_ISERIES is not set
-# CONFIG_PPC_EARLY_DEBUG_PAS_REALMODE is not set
-# CONFIG_PPC_EARLY_DEBUG_BEAT is not set
-# CONFIG_PPC_EARLY_DEBUG_44x is not set
-# CONFIG_PPC_EARLY_DEBUG_CPM is not set
 
 #
 # Security options
@@ -997,8 +1025,12 @@ CONFIG_CRYPTO_DES=y
 # CONFIG_CRYPTO_ARC4 is not set
 # CONFIG_CRYPTO_KHAZAD is not set
 # CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_SEED is not set
 # CONFIG_CRYPTO_DEFLATE is not set
 # CONFIG_CRYPTO_MICHAEL_MIC is not set
 # CONFIG_CRYPTO_CRC32C is not set
 # CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_AUTHENC is not set
 CONFIG_CRYPTO_HW=y
+# CONFIG_PPC_CLOCK is not set
+CONFIG_PPC_LIB_RHEAP=y
-- 
1.5.3.4

^ permalink raw reply related

* Re: Xilinx ML310 Linux 2.6 PCI bridge
From: Jean-Samuel Chenard @ 2007-12-10 16:33 UTC (permalink / raw)
  To: Stephen Neuendorffer; +Cc: linuxppc-embedded
In-Reply-To: <20071210054659.D373C16006E@mail51-dub.bigfish.com>

On Dec 10, 2007 12:46 AM, Stephen Neuendorffer
<stephen.neuendorffer@xilinx.com> wrote:
> I would strongly recommend not spending a huge amount of time on the ml31=
0.
> The University program board is dirt cheap for universities, very well
> supported under Linux, using either secretlab.ca or git.xilinx.com and yo=
u
> won't spend your time getting PCI to place and route correctly, followed =
by
> figuring out how to get Linux to talk to it nicely.

Hi Stephen,

Thanks for the insight.  I was aware of the XUP V2P board and I think
this is a nice initiative to let students use some great technology.

>  If your lab can afford a bee2, then you should be able to afford one (or
> better yet, several) xupv2p's to go with it.

That's where things get complicated...  In our lab, we get expensive
hardware such as the BEE2 via CMC Microsystems (a Canadian granting
agency).  However, getting funds locally to buy other equipment is a
relatively long and administratively complex process...

One of my colleague did not use his ML-310 board so I got a hold of it
(in a day, with little overhead).  So far, the ML-310 has helped me
bring up Linux 2.6 and experiment most of what I need to use on the
BEE2.  I am only missing the networking.  This would make things much
nicer to work via a NFS-exported root file system.  However, I'm OK
swapping the CompactFlash card...

I find it strange that Xilinx would not invest in supporting Linux 2.6
for an important feature such as a PCI bridge (not specifically for
the ML-310, but in general) when this can bring many interesting
benefits to an embedded platform.  Is this something that is "in
progress" at Xilinx?  You seem to imply that most of the difficulty in
getting the PCI core working is in meeting the timing requirements
(not with the SW drivers), so I might be missing driver files when I
generate the BSP code for Linux 2.6.

Regards,

Jean-Samuel
--=20
Integrated Microsystems Laboratory
McGill University, Montr=E9al, QC, CANADA
Web Page: http://chaos.ece.mcgill.ca

^ permalink raw reply

* Re: [PATCH 3/4] Convert PowerPC MPC i2c to of_platform_driver from platform_driver
From: Jon Smirl @ 2007-12-10 16:12 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev, i2c
In-Reply-To: <1E5FF6FC-E905-4FAF-84ED-A96B0E0ACCA8@kernel.crashing.org>

On 12/10/07, Kumar Gala <galak@kernel.crashing.org> wrote:
>
> On Dec 9, 2007, at 5:36 PM, Jon Smirl wrote:
>
> > Convert MPC i2c driver from being a platform_driver to an open
> > firmware version. Error returns were improved. Routine names were
> > changed from fsl_ to mpc_ to make them match the file name.
> >
> > Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
> > ---
> >
> > arch/powerpc/sysdev/fsl_soc.c |   96 ---------------------
> > drivers/i2c/busses/i2c-mpc.c  |  190 +++++++++++++++++++++++++++
> > +-------------
> > 2 files changed, 129 insertions(+), 157 deletions(-)
>
> this patch breaks arch/ppc users which are platform_driver based and
> I'm guessing a number of arch/powerpc users which don't
> of_platform_bus_probe().

We should just make two copies of the driver. All of this is needed to
allow automatic loading of the correct i2c modules as dictated by the
device tree. I'm working on the same support for automatically loading
audio codec drivers.

> Also, you should split out the conversion and error fixing into
> separate patches.

I can spin another revision to move those nine lines adding missing
error returns to another patch.

>
> - k
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: [PATCH] windfarm: add PowerMac 12,1 support
From: Arnd Bergmann @ 2007-12-10 15:39 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1197299517.6822.2.camel@thilivren>

On Monday 10 December 2007, =C3=89tienne Bersac wrote:
> From: =C3=89tienne Bersac <bersace03@gmail.com>
>=20
> Here is an updated patch that fix the potential bug, CPU param detection
> failure might not be detected. Please review it.=20

I didn't find much interesting to complain about, just a few style
issues that could be improved. Good work!

> -static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr)
> +static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr,
> +			      const char *loc)
>  {
>  	struct wf_6690_sensor *max;
> -	char *name =3D "backside-temp";
> +	char *name =3D NULL;
> =20

It's better not to initialize local variables like this one, instead of
assigning them to 0 or NULL. When it's not initialized, gcc is able to
warn about cases where you don't assign a meaningful value before using
it.

> +#undef	DEBUG
> +
> +#ifdef DEBUG
> +#define DBG(args...)	printk(args)
> +#else
> +#define DBG(args...)	do { } while (0)
> +#endif

Don't define your own macros like these, but instead use the ones that
are there. If possible, use 'dev_dbg()', otherwise 'pr_debug()'.

> +static int pm121_mach_model;	/* machine model id */
> +
> +/* Controls & sensors */
> +static struct wf_sensor	*sensor_cpu_power;
> +static struct wf_sensor	*sensor_cpu_temp;
> +static struct wf_sensor	*sensor_cpu_voltage;
> +static struct wf_sensor	*sensor_cpu_current;
> +static struct wf_sensor	*sensor_gpu_temp;
> +static struct wf_sensor	*sensor_north_bridge_temp;
> +static struct wf_sensor	*sensor_hard_drive_temp;
> +static struct wf_sensor	*sensor_optical_drive_temp;
> +static struct wf_sensor	*sensor_incoming_air_temp; /* unused ! */
> +static struct wf_control *controls[N_CONTROLS] =3D {};
> +
> +static unsigned int pm121_failure_state;
> +static int pm121_readjust, pm121_skipping;
> +static s32 average_power;

Having many global variables is typically a sign that you are doing
something wrong. It's better to attach all the data you have to your
device structure.

Have you checked that you are doing the right locking when accessing
these variables from concurrent threads?

> +struct pm121_correction {
> +	int	offset;
> +	int	slope;
> +};
> +
> +struct pm121_correction corrections[N_CONTROLS][PM121_NUM_CONFIGS] =3D {

This looks like it could be 'const'. 'static const' variables are better
because it becomes obvious that you don't need locking to access them.

> +static struct pm121_connection pm121_connections[] =3D {

same here.

> +static struct pm121_sys_param
> +pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] =3D {

and here.


> +#ifdef HACKED_OVERTEMP
> +#define	MAX	0x4a0000
> +#else
> +#define	MAX	st->pid.param.tmax
> +#endif
> +	if (temp > MAX)
> +		pm121_failure_state |=3D FAILURE_OVERTEMP;
> +
> +#undef	MAX

You don't define HACKED_OVERTEMP anywhere, so you basicall have dead
code here. It's probably better to remove that for the upstream version,
even if it was helpful for your debugging.

> +
> +#define	pm121_register_control(control, match, id)			\
> +	if (controls[id] =3D=3D NULL && !strcmp(control->name,match)) {	\
> +		if (wf_get_control(control) =3D=3D 0)			\
> +			controls[id] =3D control;				\
> +	}								\
> +	all =3D all && controls[id];

Does this need to be a macro? If possible, use static functions
for this kind of stuff. They will normally be inlined, which gives
you the same object code in the end.

> +static int __init pm121_init(void)
> +{
> +	int rc =3D -ENODEV;
> +
> +	if (machine_is_compatible("PowerMac12,1"))
> +		rc =3D pm121_init_pm();
> +
> +	if (rc =3D=3D 0) {
> +#ifdef MODULE
> +		request_module("windfarm_smu_controls");
> +		request_module("windfarm_smu_sensors");
> +		request_module("windfarm_smu_sat");
> +		request_module("windfarm_lm75_sensor");
> +		request_module("windfarm_max6690_sensor");
> +		request_module("windfarm_cpufreq_clamp");
> +
> +#endif /* MODULE */
> +		platform_driver_register(&pm121_driver);
> +	}
> +
> +	return rc;
> +}

Why the #ifdef MODULE here? It's normally better to have identical
code for modular and built-in cases.

	Arnd <><

^ permalink raw reply

* Re: [PATCH 3/4] Convert PowerPC MPC i2c to of_platform_driver from platform_driver
From: Kumar Gala @ 2007-12-10 15:34 UTC (permalink / raw)
  To: Jon Smirl; +Cc: linuxppc-dev, i2c
In-Reply-To: <20071209233654.2327.18925.stgit@terra.home>


On Dec 9, 2007, at 5:36 PM, Jon Smirl wrote:

> Convert MPC i2c driver from being a platform_driver to an open  
> firmware version. Error returns were improved. Routine names were  
> changed from fsl_ to mpc_ to make them match the file name.
>
> Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
> ---
>
> arch/powerpc/sysdev/fsl_soc.c |   96 ---------------------
> drivers/i2c/busses/i2c-mpc.c  |  190 +++++++++++++++++++++++++++ 
> +-------------
> 2 files changed, 129 insertions(+), 157 deletions(-)

this patch breaks arch/ppc users which are platform_driver based and  
I'm guessing a number of arch/powerpc users which don't  
of_platform_bus_probe().

Also, you should split out the conversion and error fixing into  
separate patches.

- k

^ permalink raw reply

* Re: [PATCH] windfarm: add PowerMac 12,1 support
From: Étienne Bersac @ 2007-12-10 15:11 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1196955051.6862.10.camel@thilivren>

From: Étienne Bersac <bersace03@gmail.com>

Hi,

Here is an updated patch that fix the potential bug, CPU param detection
failure might not be detected. Please review it. 

Regards,
Étienne.

Signed-off-by: Étienne Bersac <bersace03@gmail.com>
---
Implement fan control for PowerMac 12,1 machines. This needs update to
windfarm_lm75 and windfarm_max6690 sensors drivers in order to properly
match sensors by their location. The code is based on windfarm_pm81
driver, adapted to the complexity of PowerMac 12,1 : 4 system control
loops instead of one, per-control target correction and per-model
control rubber-banding.

All the data are copied from Darwin 8.10 AppleMacRISC4PE-185.0.0 module
as well as correction formula.

diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig
index 0a6fa1f..7125837 100644
--- a/arch/powerpc/configs/g5_defconfig
+++ b/arch/powerpc/configs/g5_defconfig
@@ -656,6 +656,7 @@ CONFIG_WINDFARM=y
 CONFIG_WINDFARM_PM81=y
 CONFIG_WINDFARM_PM91=y
 CONFIG_WINDFARM_PM112=y
+CONFIG_WINDFARM_PM121=y
 # CONFIG_PMAC_RACKMETER is not set
 CONFIG_NETDEVICES=y
 # CONFIG_NETDEVICES_MULTIQUEUE is not set
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index 77f50b6..2d028e6 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -234,6 +234,14 @@ config WINDFARM_PM112
 	  which are the recent dual and quad G5 machines using the
 	  970MP dual-core processor.
 
+config WINDFARM_PM121
+	tristate "Support for thermal management on PowerMac12,1"
+	depends on WINDFARM && I2C && PMAC_SMU
+	select I2C_POWERMAC
+	help
+	  This driver provides thermal control for the PowerMac12,1
+	  which is the iMac G5 (iSight) 970MP
+
 config ANSLCD
 	tristate "Support for ANS LCD display"
 	depends on ADB_CUDA && PPC_PMAC
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index 2dfc3f4..e3132ef 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -42,4 +42,9 @@ obj-$(CONFIG_WINDFARM_PM112)	+= windfarm_pm112.o windfarm_smu_sat.o \
 				   windfarm_smu_sensors.o \
 				   windfarm_max6690_sensor.o \
 				   windfarm_lm75_sensor.o windfarm_pid.o
+obj-$(CONFIG_WINDFARM_PM121)	+= windfarm_pm121.o windfarm_smu_sat.o \
+				   windfarm_smu_controls.o \
+				   windfarm_smu_sensors.o \
+				   windfarm_max6690_sensor.o \
+				   windfarm_lm75_sensor.o windfarm_pid.o
 obj-$(CONFIG_PMAC_RACKMETER)	+= rack-meter.o
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index 7e10c3a..b92b959 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -127,6 +127,12 @@ static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
 	 */
 	if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY"))
 		lm->sens.name = "hd-temp";
+	else if (!strcmp(loc, "Incoming Air Temp"))
+		lm->sens.name = "incoming-air-temp";
+	else if (!strcmp(loc, "ODD Temp"))
+		lm->sens.name = "optical-drive-temp";
+	else if (!strcmp(loc, "HD Temp"))
+		lm->sens.name = "hard-drive-temp";
 	else
 		goto fail;
 
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c
index 5f03aab..c2b6d1c 100644
--- a/drivers/macintosh/windfarm_max6690_sensor.c
+++ b/drivers/macintosh/windfarm_max6690_sensor.c
@@ -77,10 +77,11 @@ static struct wf_sensor_ops wf_max6690_ops = {
 	.owner		= THIS_MODULE,
 };
 
-static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr)
+static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr,
+			      const char *loc)
 {
 	struct wf_6690_sensor *max;
-	char *name = "backside-temp";
+	char *name = NULL;
 
 	max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
 	if (max == NULL) {
@@ -89,6 +90,15 @@ static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr)
 		return;
 	}
 
+	if (!strcmp(loc, "BACKSIDE"))
+		name = "backside-temp";
+	else if (!strcmp(loc, "NB Ambient"))
+		name = "north-bridge-temp";
+	else if (!strcmp(loc, "GPU Ambient"))
+		name = "gpu-temp";
+	else
+		goto fail;
+
 	max->sens.ops = &wf_max6690_ops;
 	max->sens.name = name;
 	max->i2c.addr = addr >> 1;
@@ -138,9 +148,7 @@ static int wf_max6690_attach(struct i2c_adapter *adapter)
 		if (loc == NULL || addr == 0)
 			continue;
 		printk("found max6690, loc=%s addr=0x%02x\n", loc, addr);
-		if (strcmp(loc, "BACKSIDE"))
-			continue;
-		wf_max6690_create(adapter, addr);
+		wf_max6690_create(adapter, addr, loc);
 	}
 
 	return 0;
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
new file mode 100644
index 0000000..e3a9c50
--- /dev/null
+++ b/drivers/macintosh/windfarm_pm121.c
@@ -0,0 +1,1053 @@
+/*
+ * Windfarm PowerMac thermal control. iMac G5 iSight
+ *
+ * (c) Copyright 2007 Étienne Bersac <bersace@gmail.com>
+ *
+ * Bits & pieces from windfarm_pm81.c by (c) Copyright 2005 Benjamin
+ * Herrenschmidt, IBM Corp. <benh@kernel.crashing.org>
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ *
+ *
+ * PowerMac12,1
+ * ============
+ *
+ *
+ * The algorithm used is the PID control algorithm, used the same way
+ * the published Darwin code does, using the same values that are
+ * present in the Darwin 8.10 snapshot property lists (note however
+ * that none of the code has been re-used, it's a complete
+ * re-implementation
+ *
+ * There is two models using PowerMac12,1. Model 2 is iMac G5 iSight
+ * 17" while Model 3 is iMac G5 20". They do have both the same
+ * controls with a tiny difference. The control-ids of hard-drive-fan
+ * and cpu-fan is swapped.
+ *
+ *
+ * Target Correction :
+ *
+ * controls have a target correction calculated as :
+ *
+ * new_min = ((((average_power * slope) >> 16) + offset) >> 16) + min_value
+ * new_value = max(new_value, max(new_min, 0))
+ *
+ * OD Fan control correction.
+ *
+ * # model_id: 2
+ *   offset		: -19563152
+ *   slope		:  1956315
+ *
+ * # model_id: 3
+ *   offset		: -15650652
+ *   slope		:  1565065
+ *
+ * HD Fan control correction.
+ *
+ * # model_id: 2
+ *   offset		: -15650652
+ *   slope		:  1565065
+ *
+ * # model_id: 3
+ *   offset		: -19563152
+ *   slope		:  1956315
+ *
+ * CPU Fan control correction.
+ *
+ * # model_id: 2
+ *   offset		: -25431900
+ *   slope		:  2543190
+ *
+ * # model_id: 3
+ *   offset		: -15650652
+ *   slope		:  1565065
+ *
+ *
+ * Target rubber-banding :
+ *
+ * Some controls have a target correction which depends on another
+ * control value. The correction is computed in the following way :
+ *
+ * new_min = ref_value * slope + offset
+ *
+ * ref_value is the value of the reference control. If new_min is
+ * greater than 0, then we correct the target value using :
+ *
+ * new_target = max (new_target, new_min >> 16)
+ *
+ *
+ * # model_id : 2
+ *   control	: cpu-fan
+ *   ref	: optical-drive-fan
+ *   offset	: -15650652
+ *   slope	: 1565065
+ *
+ * # model_id : 3
+ *   control	: optical-drive-fan
+ *   ref	: hard-drive-fan
+ *   offset	: -32768000
+ *   slope	: 65536
+ *
+ *
+ * In order to have the moste efficient correction with those
+ * dependencies, we must trigger HD loop before OD loop before CPU
+ * loop.
+ *
+ *
+ * The various control loops found in Darwin config file are:
+ *
+ * HD Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : hard-drive-fan
+ *   sensor         : hard-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x002D70A3
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x370000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : hard-drive-fan
+ *   sensor         : hard-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x002170A3
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x370000
+ *                    Interval = 5s
+ *
+ * OD Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : optical-drive-fan
+ *   sensor         : optical-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x001FAE14
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x320000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : optical-drive-fan
+ *   sensor         : optical-drive-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x001FAE14
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x320000
+ *                    Interval = 5s
+ *
+ * GPU Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : hard-drive-fan
+ *   sensor         : gpu-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x002A6666
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x5A0000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : cpu-fan
+ *   sensor         : gpu-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x0010CCCC
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x500000
+ *                    Interval = 5s
+ *
+ * KODIAK (aka northbridge) Fan control loop.
+ *
+ * # model_id: 2
+ *   control        : optical-drive-fan
+ *   sensor         : north-bridge-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x003BD70A
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x550000
+ *                    Interval = 5s
+ *
+ * # model_id: 3
+ *   control        : hard-drive-fan
+ *   sensor         : north-bridge-temp
+ *   PID params     : G_d = 0x00000000
+ *                    G_p = 0x0030F5C2
+ *                    G_r = 0x00019999
+ *                    History = 2 entries
+ *                    Input target = 0x550000
+ *                    Interval = 5s
+ *
+ * CPU Fan control loop.
+ *
+ *   control        : cpu-fan
+ *   sensors        : cpu-temp, cpu-power
+ *   PID params     : from SDB partition
+ *
+ *
+ * CPU Slew control loop.
+ *
+ *   control        : cpufreq-clamp
+ *   sensor         : cpu-temp
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/kmod.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+
+#define VERSION "0.4"
+
+#undef	DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while (0)
+#endif
+
+static int pm121_mach_model;	/* machine model id */
+
+/* Controls & sensors */
+static struct wf_sensor	*sensor_cpu_power;
+static struct wf_sensor	*sensor_cpu_temp;
+static struct wf_sensor	*sensor_cpu_voltage;
+static struct wf_sensor	*sensor_cpu_current;
+static struct wf_sensor	*sensor_gpu_temp;
+static struct wf_sensor	*sensor_north_bridge_temp;
+static struct wf_sensor	*sensor_hard_drive_temp;
+static struct wf_sensor	*sensor_optical_drive_temp;
+static struct wf_sensor	*sensor_incoming_air_temp; /* unused ! */
+
+enum {
+	FAN_CPU,
+	FAN_HD,
+	FAN_OD,
+	CPUFREQ,
+	N_CONTROLS
+};
+static struct wf_control *controls[N_CONTROLS] = {};
+
+/* Set to kick the control loop into life */
+static int pm121_all_controls_ok, pm121_all_sensors_ok, pm121_started;
+
+enum {
+	FAILURE_FAN		= 1 << 0,
+	FAILURE_SENSOR		= 1 << 1,
+	FAILURE_OVERTEMP	= 1 << 2
+};
+
+/* All sys loops. Note the HD before the OD loop in order to have it
+   run before. */
+enum {
+	LOOP_GPU,		/* control = hd or cpu, but luckily,
+				   it doesn't matter */
+	LOOP_HD,		/* control = hd */
+	LOOP_KODIAK,		/* control = hd or od */
+	LOOP_OD,		/* control = od */
+	N_LOOPS
+};
+
+static const char *loop_names[N_LOOPS] = {
+	"GPU",
+	"HD",
+	"KODIAK",
+	"OD",
+};
+
+#define	PM121_NUM_CONFIGS	2
+
+static unsigned int pm121_failure_state;
+static int pm121_readjust, pm121_skipping;
+static s32 average_power;
+
+struct pm121_correction {
+	int	offset;
+	int	slope;
+};
+
+struct pm121_correction corrections[N_CONTROLS][PM121_NUM_CONFIGS] = {
+	/* FAN_OD */
+	{
+		/* MODEL 2 */
+		{ .offset	= -19563152,
+		  .slope	=  1956315
+		},
+		/* MODEL 3 */
+		{ .offset	= -15650652,
+		  .slope	=  1565065
+		},
+	},
+	/* FAN_HD */
+	{
+		/* MODEL 2 */
+		{ .offset	= -15650652,
+		  .slope	=  1565065
+		},
+		/* MODEL 3 */
+		{ .offset	= -19563152,
+		  .slope	=  1956315
+		},
+	},
+	/* FAN_CPU */
+	{
+		/* MODEL 2 */
+		{ .offset	= -25431900,
+		  .slope	=  2543190
+		},
+		/* MODEL 3 */
+		{ .offset	= -15650652,
+		  .slope	=  1565065
+		},
+	},
+	/* CPUFREQ has no correction (and is not implemented at all) */
+};
+
+struct pm121_connection {
+	unsigned int	control_id;
+	unsigned int	ref_id;
+	struct pm121_correction	correction;
+};
+
+static struct pm121_connection pm121_connections[] = {
+	/* MODEL 2 */
+	{ .control_id	= FAN_CPU,
+	  .ref_id	= FAN_OD,
+	  { .offset	= -32768000,
+	    .slope	=  65536
+	  }
+	},
+	/* MODEL 3 */
+	{ .control_id	= FAN_OD,
+	  .ref_id	= FAN_HD,
+	  { .offset	= -32768000,
+	    .slope	=  65536
+	  }
+	},
+};
+
+/* pointer to the current model connection */
+static struct pm121_connection *pm121_connection;
+
+/*
+ * ****** System Fans Control Loop ******
+ *
+ */
+
+/* Since each loop handles only one control and we want to avoid
+ * writing virtual control, we store the control correction with the
+ * loop params. Some data are not set, there are common to all loop
+ * and thus, hardcoded.
+ */
+struct pm121_sys_param {
+	/* purely informative since we use mach_model-2 as index */
+	int			model_id;
+	struct wf_sensor	**sensor; /* use sensor_id instead ? */
+	s32			gp, itarget;
+	unsigned int		control_id;
+};
+
+static struct pm121_sys_param
+pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] = {
+	/* GPU Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_gpu_temp,
+		  .gp		= 0x002A6666,
+		  .itarget	= 0x5A0000,
+		  .control_id	= FAN_HD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_gpu_temp,
+		  .gp		= 0x0010CCCC,
+		  .itarget	= 0x500000,
+		  .control_id	= FAN_CPU,
+		},
+	},
+	/* HD Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_hard_drive_temp,
+		  .gp		= 0x002D70A3,
+		  .itarget	= 0x370000,
+		  .control_id	= FAN_HD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_hard_drive_temp,
+		  .gp		= 0x002170A3,
+		  .itarget	= 0x370000,
+		  .control_id	= FAN_HD,
+		},
+	},
+	/* KODIAK Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_north_bridge_temp,
+		  .gp		= 0x003BD70A,
+		  .itarget	= 0x550000,
+		  .control_id	= FAN_OD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_north_bridge_temp,
+		  .gp		= 0x0030F5C2,
+		  .itarget	= 0x550000,
+		  .control_id	= FAN_HD,
+		},
+	},
+	/* OD Fan control loop */
+	{
+		{ .model_id	= 2,
+		  .sensor	= &sensor_optical_drive_temp,
+		  .gp		= 0x001FAE14,
+		  .itarget	= 0x320000,
+		  .control_id	= FAN_OD,
+		},
+		{ .model_id	= 3,
+		  .sensor	= &sensor_optical_drive_temp,
+		  .gp		= 0x001FAE14,
+		  .itarget	= 0x320000,
+		  .control_id	= FAN_OD,
+		},
+	},
+};
+
+/* the hardcoded values */
+#define	PM121_SYS_GD		0x00000000
+#define	PM121_SYS_GR		0x00019999
+#define	PM121_SYS_HISTORY_SIZE	2
+#define	PM121_SYS_INTERVAL	5
+
+/* State data used by the system fans control loop
+ */
+struct pm121_sys_state {
+	int			ticks;
+	s32			setpoint;
+	struct wf_pid_state	pid;
+};
+
+struct pm121_sys_state *pm121_sys_state[N_LOOPS] = {};
+
+/*
+ * ****** CPU Fans Control Loop ******
+ *
+ */
+
+#define PM121_CPU_INTERVAL	1
+
+/* State data used by the cpu fans control loop
+ */
+struct pm121_cpu_state {
+	int			ticks;
+	s32			setpoint;
+	struct wf_cpu_pid_state	pid;
+};
+
+static struct pm121_cpu_state *pm121_cpu_state;
+
+
+
+/*
+ * ***** Implementation *****
+ *
+ */
+
+/* correction the value using the output-low-bound correction algo */
+static s32 pm121_correct(s32 new_setpoint,
+			 unsigned int control_id,
+			 s32 min)
+{
+	s32 new_min;
+	struct pm121_correction *correction;
+	correction = &corrections[control_id][pm121_mach_model - 2];
+
+	new_min = (average_power * correction->slope) >> 16;
+	new_min += correction->offset;
+	new_min = (new_min >> 16) + min;
+
+	return max(new_setpoint, max(new_min, 0));
+}
+
+static s32 pm121_connect(unsigned int control_id, s32 setpoint)
+{
+	s32 new_min, value, new_setpoint;
+
+	if (pm121_connection->control_id == control_id) {
+		controls[control_id]->ops->get_value(controls[control_id],
+						     &value);
+		new_min = value * pm121_connection->correction.slope;
+		new_min += pm121_connection->correction.offset;
+		if (new_min > 0) {
+			new_setpoint = max(setpoint, (new_min >> 16));
+			if (new_setpoint != setpoint) {
+				DBG("pm121: %s depending on %s, "
+				    "corrected from %d to %d RPM\n",
+				    controls[control_id]->name,
+				    controls[pm121_connection->ref_id]->name,
+				    (int) setpoint, (int) new_setpoint);
+			}
+		} else
+			new_setpoint = setpoint;
+	}
+	/* no connection */
+	else
+		new_setpoint = setpoint;
+
+	return new_setpoint;
+}
+
+/* FAN LOOPS */
+static void pm121_create_sys_fans(int loop_id)
+{
+	struct pm121_sys_param *param = NULL;
+	struct wf_pid_param pid_param;
+	struct wf_control *control = NULL;
+	int i;
+
+	/* First, locate the params for this model */
+	for (i = 0; i < PM121_NUM_CONFIGS; i++) {
+		if (pm121_sys_all_params[loop_id][i].model_id == pm121_mach_model) {
+			param = &(pm121_sys_all_params[loop_id][i]);
+			break;
+		}
+	}
+
+	/* No params found, put fans to max */
+	if (param == NULL) {
+		printk(KERN_WARNING "pm121: %s fan config not found "
+		       " for this machine model\n",
+		       loop_names[loop_id]);
+		goto fail;
+	}
+
+	control = controls[param->control_id];
+
+	/* Alloc & initialize state */
+	pm121_sys_state[loop_id] = kmalloc(sizeof(struct pm121_sys_state),
+					   GFP_KERNEL);
+	if (pm121_sys_state[loop_id] == NULL) {
+		printk(KERN_WARNING "pm121: Memory allocation error\n");
+		goto fail;
+	}
+	pm121_sys_state[loop_id]->ticks = 1;
+
+	/* Fill PID params */
+	pid_param.gd		= PM121_SYS_GD;
+	pid_param.gp		= param->gp;
+	pid_param.gr		= PM121_SYS_GR;
+	pid_param.interval	= PM121_SYS_INTERVAL;
+	pid_param.history_len	= PM121_SYS_HISTORY_SIZE;
+	pid_param.itarget	= param->itarget;
+	pid_param.min		= control->ops->get_min(control);
+	pid_param.max		= control->ops->get_max(control);
+
+	wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param);
+
+	DBG("pm121: %s Fan control loop initialized.\n"
+	    "       itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
+	    loop_names[loop_id], FIX32TOPRINT(pid_param.itarget),
+	    pid_param.min, pid_param.max);
+	return;
+
+ fail:
+	/* note that this is not optimal since another loop may still
+	   control the same control */
+	printk(KERN_WARNING "pm121: failed to set up %s loop "
+	       "setting \"%s\" to max speed.\n",
+	       loop_names[loop_id], control->name);
+
+	if (control)
+		wf_control_set_max(control);
+}
+
+static void pm121_sys_fans_tick(int loop_id)
+{
+	struct pm121_sys_param *param;
+	struct pm121_sys_state *st;
+	struct wf_sensor *sensor;
+	struct wf_control *control;
+	s32 temp, new_setpoint;
+	int rc;
+
+	param = &(pm121_sys_all_params[loop_id][pm121_mach_model-2]);
+	st = pm121_sys_state[loop_id];
+	sensor = *(param->sensor);
+	control = controls[param->control_id];
+
+	if (--st->ticks != 0) {
+		if (pm121_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = PM121_SYS_INTERVAL;
+
+	rc = sensor->ops->get_value(sensor, &temp);
+	if (rc) {
+		printk(KERN_WARNING "windfarm: %s sensor error %d\n",
+		       sensor->name, rc);
+		pm121_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	DBG("pm121: %s Fan tick ! %s: %d.%03d\n",
+	    loop_names[loop_id], sensor->name,
+	    FIX32TOPRINT(temp));
+
+	new_setpoint = wf_pid_run(&st->pid, temp);
+
+	/* correction */
+	new_setpoint = pm121_correct(new_setpoint,
+				     param->control_id,
+				     st->pid.param.min);
+	/* linked corretion */
+	new_setpoint = pm121_connect(param->control_id, new_setpoint);
+
+	if (new_setpoint == st->setpoint)
+		return;
+	st->setpoint = new_setpoint;
+	DBG("pm121: %s corrected setpoint: %d RPM\n",
+	    control->name, (int)new_setpoint);
+ readjust:
+	if (control && pm121_failure_state == 0) {
+		rc = control->ops->set_value(control, st->setpoint);
+		if (rc) {
+			printk(KERN_WARNING "windfarm: %s fan error %d\n",
+			       control->name, rc);
+			pm121_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+
+/* CPU LOOP */
+static void pm121_create_cpu_fans(void)
+{
+	struct wf_cpu_pid_param pid_param;
+	const struct smu_sdbp_header *hdr;
+	struct smu_sdbp_cpupiddata *piddata;
+	struct smu_sdbp_fvt *fvt;
+	struct wf_control *fan_cpu;
+	s32 tmax, tdelta, maxpow, powadj;
+
+	fan_cpu = controls[FAN_CPU];
+
+	/* First, locate the PID params in SMU SBD */
+	hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
+	if (hdr == 0) {
+		printk(KERN_WARNING "pm121: CPU PID fan config not found.\n");
+		goto fail;
+	}
+	piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
+
+	/* Get the FVT params for operating point 0 (the only supported one
+	 * for now) in order to get tmax
+	 */
+	hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
+	if (hdr) {
+		fvt = (struct smu_sdbp_fvt *)&hdr[1];
+		tmax = ((s32)fvt->maxtemp) << 16;
+	} else
+		tmax = 0x5e0000; /* 94 degree default */
+
+	/* Alloc & initialize state */
+	pm121_cpu_state = kmalloc(sizeof(struct pm121_cpu_state),
+				  GFP_KERNEL);
+	if (pm121_cpu_state == NULL)
+		goto fail;
+	pm121_cpu_state->ticks = 1;
+
+	/* Fill PID params */
+	pid_param.interval = PM121_CPU_INTERVAL;
+	pid_param.history_len = piddata->history_len;
+	if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
+		printk(KERN_WARNING "pm121: History size overflow on "
+		       "CPU control loop (%d)\n", piddata->history_len);
+		pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
+	}
+	pid_param.gd = piddata->gd;
+	pid_param.gp = piddata->gp;
+	pid_param.gr = piddata->gr / pid_param.history_len;
+
+	tdelta = ((s32)piddata->target_temp_delta) << 16;
+	maxpow = ((s32)piddata->max_power) << 16;
+	powadj = ((s32)piddata->power_adj) << 16;
+
+	pid_param.tmax = tmax;
+	pid_param.ttarget = tmax - tdelta;
+	pid_param.pmaxadj = maxpow - powadj;
+
+	pid_param.min = fan_cpu->ops->get_min(fan_cpu);
+	pid_param.max = fan_cpu->ops->get_max(fan_cpu);
+
+	wf_cpu_pid_init(&pm121_cpu_state->pid, &pid_param);
+
+	DBG("pm121: CPU Fan control initialized.\n");
+	DBG("       ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM,\n",
+	    FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
+	    pid_param.min, pid_param.max);
+
+	return;
+
+ fail:
+	printk(KERN_WARNING "pm121: CPU fan config not found, max fan speed\n");
+
+	if (controls[CPUFREQ])
+		wf_control_set_max(controls[CPUFREQ]);
+	if (fan_cpu)
+		wf_control_set_max(fan_cpu);
+}
+
+
+static void pm121_cpu_fans_tick(struct pm121_cpu_state *st)
+{
+	s32 new_setpoint, temp, power;
+	struct wf_control *fan_cpu = NULL;
+	int rc;
+
+	if (--st->ticks != 0) {
+		if (pm121_readjust)
+			goto readjust;
+		return;
+	}
+	st->ticks = PM121_CPU_INTERVAL;
+
+	fan_cpu = controls[FAN_CPU];
+
+	rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+	if (rc) {
+		printk(KERN_WARNING "pm121: CPU temp sensor error %d\n",
+		       rc);
+		pm121_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+	if (rc) {
+		printk(KERN_WARNING "pm121: CPU power sensor error %d\n",
+		       rc);
+		pm121_failure_state |= FAILURE_SENSOR;
+		return;
+	}
+
+	DBG("pm121: CPU Fans tick ! CPU temp: %d.%03d°C, power: %d.%03d\n",
+	    FIX32TOPRINT(temp), FIX32TOPRINT(power));
+
+#ifdef HACKED_OVERTEMP
+#define	MAX	0x4a0000
+#else
+#define	MAX	st->pid.param.tmax
+#endif
+	if (temp > MAX)
+		pm121_failure_state |= FAILURE_OVERTEMP;
+
+#undef	MAX
+
+	new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
+
+	/* correction */
+	new_setpoint = pm121_correct(new_setpoint,
+				     FAN_CPU,
+				     st->pid.param.min);
+
+	/* connected correction */
+	new_setpoint = pm121_connect(FAN_CPU, new_setpoint);
+
+	if (st->setpoint == new_setpoint)
+		return;
+	st->setpoint = new_setpoint;
+	DBG("pm121: CPU corrected setpoint: %d RPM\n", (int)new_setpoint);
+
+ readjust:
+	if (fan_cpu && pm121_failure_state == 0) {
+		rc = fan_cpu->ops->set_value(fan_cpu, st->setpoint);
+		if (rc) {
+			printk(KERN_WARNING "pm121: %s fan error %d\n",
+			       fan_cpu->name, rc);
+			pm121_failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+/*
+ * ****** Common ******
+ *
+ */
+
+static void pm121_tick(void)
+{
+	unsigned int last_failure = pm121_failure_state;
+	unsigned int new_failure;
+	s32 total_power;
+	int i;
+
+	if (!pm121_started) {
+		DBG("pm121: creating control loops !\n");
+		for (i = 0; i < N_LOOPS; i++)
+			pm121_create_sys_fans(i);
+
+		pm121_create_cpu_fans();
+		pm121_started = 1;
+	}
+
+	/* skipping ticks */
+	if (pm121_skipping && --pm121_skipping)
+		return;
+
+	/* compute average power */
+	total_power = 0;
+	for (i = 0; i < pm121_cpu_state->pid.param.history_len; i++)
+		total_power += pm121_cpu_state->pid.powers[i];
+
+	average_power = total_power / pm121_cpu_state->pid.param.history_len;
+
+
+	pm121_failure_state = 0;
+	for (i = 0 ; i < N_LOOPS; i++) {
+		if (pm121_sys_state[i])
+			pm121_sys_fans_tick(i);
+	}
+
+	if (pm121_cpu_state)
+		pm121_cpu_fans_tick(pm121_cpu_state);
+
+	pm121_readjust = 0;
+	new_failure = pm121_failure_state & ~last_failure;
+
+	/* If entering failure mode, clamp cpufreq and ramp all
+	 * fans to full speed.
+	 */
+	if (pm121_failure_state && !last_failure) {
+		for (i = 0; i < N_CONTROLS; i++) {
+			if (controls[i])
+				wf_control_set_max(controls[i]);
+		}
+	}
+
+	/* If leaving failure mode, unclamp cpufreq and readjust
+	 * all fans on next iteration
+	 */
+	if (!pm121_failure_state && last_failure) {
+		if (controls[CPUFREQ])
+			wf_control_set_min(controls[CPUFREQ]);
+		pm121_readjust = 1;
+	}
+
+	/* Overtemp condition detected, notify and start skipping a couple
+	 * ticks to let the temperature go down
+	 */
+	if (new_failure & FAILURE_OVERTEMP) {
+		wf_set_overtemp();
+		pm121_skipping = 2;
+	}
+
+	/* We only clear the overtemp condition if overtemp is cleared
+	 * _and_ no other failure is present. Since a sensor error will
+	 * clear the overtemp condition (can't measure temperature) at
+	 * the control loop levels, but we don't want to keep it clear
+	 * here in this case
+	 */
+	if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
+		wf_clear_overtemp();
+}
+
+
+
+
+#define	pm121_register_control(control, match, id)			\
+	if (controls[id] == NULL && !strcmp(control->name,match)) {	\
+		if (wf_get_control(control) == 0)			\
+			controls[id] = control;				\
+	}								\
+	all = all && controls[id];
+
+static void pm121_new_control(struct wf_control *ct)
+{
+	int all = 1;
+
+	if (pm121_all_controls_ok)
+		return;
+
+	pm121_register_control(ct, "optical-drive-fan",	FAN_OD);
+	pm121_register_control(ct, "hard-drive-fan",	FAN_HD);
+	pm121_register_control(ct, "cpu-fan",		FAN_CPU);
+	pm121_register_control(ct, "cpufreq-clamp",	CPUFREQ);
+
+	if (all)
+		pm121_all_controls_ok = 1;
+}
+#undef pm121_register_control
+
+
+
+
+#define	pm121_register_sensor(sensor, match, var)		\
+	if (var == NULL && !strcmp(sensor->name,match)) {	\
+		if (wf_get_sensor(sensor) == 0)			\
+			var = sensor;				\
+	}							\
+	all = all && var;
+
+static void pm121_new_sensor(struct wf_sensor *sr)
+{
+	int all = 1;
+
+	if (pm121_all_sensors_ok)
+		return;
+
+	pm121_register_sensor(sr, "cpu-temp",
+			      sensor_cpu_temp);
+	pm121_register_sensor(sr, "cpu-current",
+			      sensor_cpu_current);
+	pm121_register_sensor(sr, "cpu-voltage",
+			      sensor_cpu_voltage);
+	pm121_register_sensor(sr, "cpu-power",
+			      sensor_cpu_power);
+	pm121_register_sensor(sr, "hard-drive-temp",
+			      sensor_hard_drive_temp);
+	pm121_register_sensor(sr, "optical-drive-temp",
+			      sensor_optical_drive_temp);
+	pm121_register_sensor(sr, "incoming-air-temp",
+			      sensor_incoming_air_temp);
+	pm121_register_sensor(sr, "north-bridge-temp",
+			      sensor_north_bridge_temp);
+	pm121_register_sensor(sr, "gpu-temp",
+			      sensor_gpu_temp);
+
+	if (all)
+		pm121_all_sensors_ok = 1;
+}
+
+#undef pm121_register_sensor
+
+
+
+static int pm121_notify(struct notifier_block *self,
+			unsigned long event, void *data)
+{
+	switch (event) {
+	case WF_EVENT_NEW_CONTROL:
+		DBG("pm121: new control %s detected\n",
+		    ((struct wf_control *)data)->name);
+		pm121_new_control(data);
+		break;
+	case WF_EVENT_NEW_SENSOR:
+		DBG("pm121: new sensor %s detected\n",
+		    ((struct wf_sensor *)data)->name);
+		pm121_new_sensor(data);
+		break;
+	case WF_EVENT_TICK:
+		if (pm121_all_controls_ok && pm121_all_sensors_ok)
+			pm121_tick();
+		break;
+	}
+
+	return 0;
+}
+
+static struct notifier_block pm121_events = {
+	.notifier_call	= pm121_notify,
+};
+
+static int pm121_init_pm(void)
+{
+	const struct smu_sdbp_header *hdr;
+
+	hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
+	if (hdr != 0) {
+		struct smu_sdbp_sensortree *st =
+			(struct smu_sdbp_sensortree *)&hdr[1];
+		pm121_mach_model = st->model_id;
+	}
+
+	pm121_connection = &pm121_connections[pm121_mach_model - 2];
+
+	printk(KERN_INFO "pm121: Initializing for iMac G5 iSight model ID %d\n",
+	       pm121_mach_model);
+
+	return 0;
+}
+
+
+static int pm121_probe(struct platform_device *ddev)
+{
+	wf_register_client(&pm121_events);
+
+	return 0;
+}
+
+static int __devexit pm121_remove(struct platform_device *ddev)
+{
+	wf_unregister_client(&pm121_events);
+	return 0;
+}
+
+static struct platform_driver pm121_driver = {
+	.probe = pm121_probe,
+	.remove = __devexit_p(pm121_remove),
+	.driver = {
+		.name = "windfarm",
+		.bus = &platform_bus_type,
+	},
+};
+
+
+static int __init pm121_init(void)
+{
+	int rc = -ENODEV;
+
+	if (machine_is_compatible("PowerMac12,1"))
+		rc = pm121_init_pm();
+
+	if (rc == 0) {
+#ifdef MODULE
+		request_module("windfarm_smu_controls");
+		request_module("windfarm_smu_sensors");
+		request_module("windfarm_smu_sat");
+		request_module("windfarm_lm75_sensor");
+		request_module("windfarm_max6690_sensor");
+		request_module("windfarm_cpufreq_clamp");
+
+#endif /* MODULE */
+		platform_driver_register(&pm121_driver);
+	}
+
+	return rc;
+}
+
+static void __exit pm121_exit(void)
+{
+
+	platform_driver_unregister(&pm121_driver);
+}
+
+
+module_init(pm121_init);
+module_exit(pm121_exit);
+
+MODULE_AUTHOR("Étienne Bersac <bersace@gmail.com>");
+MODULE_DESCRIPTION("Thermal control logic for iMac G5 (iSight)");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index 58c2590..961fa0e 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -218,6 +218,10 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
 		fct->ctrl.name = "cpu-fan";
 	else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
 		fct->ctrl.name = "drive-bay-fan";
+	else if (!strcmp(l, "HDD Fan")) /* seen on iMac G5 iSight */
+		fct->ctrl.name = "hard-drive-fan";
+	else if (!strcmp(l, "ODD Fan")) /* same */
+		fct->ctrl.name = "optical-drive-fan";
 
 	/* Unrecognized fan, bail out */
 	if (fct->ctrl.name == NULL)

^ permalink raw reply related

* Re: [PATCH] Fix rounding bug in emulation for double floatoperating
From: Kumar Gala @ 2007-12-10 15:00 UTC (permalink / raw)
  To: Liu Yu; +Cc: linuxppc-dev, David Gibson
In-Reply-To: <6EBEC19BF4A8F843BCD6B9155BBE2515C79D0D@zch01exm26.fsl.freescale.net>


On Dec 9, 2007, at 11:25 PM, Liu Yu wrote:

>
>
>
>
>
>> -----Original Message-----
>> From: David Gibson [mailto:david@gibson.dropbear.id.au]
>> Sent: Monday, December 10, 2007 12:56 PM
>> To: Liu Yu
>> Cc: linuxppc-dev@ozlabs.org
>> Subject: Re: [PATCH] Fix rounding bug in emulation for double
>> floatoperating
>>
>>
>> On Mon, Dec 10, 2007 at 01:00:52PM +0800, Liu Yu wrote:
>>>
>>> This patch fixes rounding bug in emulation for double float
>> operating on PowerPC platform.
>>>
>>> When pack double float operand, it need to truncate the
>> tail due to the limited precision.
>>> If the truncated part is not zero, the last bit of work bit
>> (totally 3 bits) need to '|' 1.
>>>
>>> This patch is completed in _FP_FRAC_SRS_2(X,N,sz)
>> (arch/powerpc/math-emu/op-2.h).
>>> Originally the code leftwards rotates the operand to just keep the
>>> truncated part, then check whether it is zero. However, the
>> number it
>>> rotates is not correct when N is not smaller than
>> _FP_W_TYPE_SIZE, and it will cause the work bit '|' 1 in the
>> improper case.
>>>
>>> This patch fixes this issue.
>>>
>>> Signed-off-by: Liu Yu <b13201@freescale.com>
>>
>> Wow someone deciphered the hideous macro hell of the math
>> emulation code enough to fix a bug.  I don't suppose you'd
>> care to fix the millions of warnings that the math-emu code
>> generates...?
>
> Oh, I don't like macro define either. But it's really a bug...


how did you find this?

- k

^ permalink raw reply

* [PATCH 3/3] Modified Documentation to explain dts entries for UCC TDM driver.
From: Poonam_Aggrwal-b10812 @ 2007-12-10 12:13 UTC (permalink / raw)
  To: rubini, kumar.gala, linux-kernel, netdev, linuxppc-dev
  Cc: michael.barkowski, rich.cutler, ashish.kalra

From: Poonam Aggrwal <b10812@freescale.com>

Modified Documentation to explain new properties introduced for UCC TDM 
driver. Also two new nodes have been added "brg" and "clocks" to configure 
a BRG from device tree.

Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
 Documentation/powerpc/booting-without-of.txt |   96 +++++++++++++++++++++++++-
 1 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index e9a3cb1..94a6b4b 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -1613,8 +1613,8 @@ platforms are moved over to use the flattened-device-tree model.
 
    Required properties:
    - device_type : should be "network", "hldc", "uart", "transparent"
-    "bisync" or "atm".
-   - compatible : could be "ucc_geth" or "fsl_atm" and so on.
+    "bisync", "atm" or "tdm".
+   - compatible : could be "ucc_geth", "fsl_atm" or "fsl,ucc_tdm" and so on.
    - model : should be "UCC".
    - device-id : the ucc number(1-8), corresponding to UCCx in UM.
    - reg : Offset and length of the register set for the device
@@ -1666,7 +1666,44 @@ platforms are moved over to use the flattened-device-tree model.
 		pio-handle = <140001>;
 	};
 
+   Required properties for tdm device_type:
+   - instead of tx-clock and rx-clock following clock properties are
+     required:
+       - fsl,tdm-tx-clk : This property selects the TX clock source for TDM
+	from a bank of clocks.
+       - fsl,tdm-rx-clk : This property selects the RX clock source for TDM
+	from a bank of clocks.
+       - fsl,tdm-tx-sync : This property selects the TX Frame sync source
+	for TDM from a bank of clocks.
+       - fsl,tdm-rx-sync : This property selects the TX Frame sync source
+	for TDM from a bank of clocks.
+
+       All the above mentioned properties are string type with possible
+       values
+       "CLK1", "CLK2", "CLK3"..."CLK24" and so on
+       "BRG1", "BRG2", "BRG3"..."BRG16" and so on
+
+  - fsl,tdm-num : TDM to be used (1,2,3 or 4 for TDMA TDMB TDMC TDMD)
+  - fsl,si-num :  Serial Interface to be used.
 
+   Example:
+	ucc@2000 {
+		device_type = "tdm";
+		compatible = "fsl,ucc-tdm";
+		model = "UCC";
+		device-id = <1>;
+		fsl,tdm-num = <1>;
+		fsl,si-num = <1>;
+		fsl,tdm-tx-clk = "CLK1";
+		fsl,tdm-rx-clk = "CLK1";
+		fsl,tdm-tx-sync = "BRG9";
+		fsl,tdm-rx-sync = "BRG9";
+		reg = <2000 200>;
+		interrupts = <20>;
+		interrupt-parent = <&qeic>;
+		pio-handle = <&ucc1pio>;
+	};
+
    v) Parallel I/O Ports
 
    This node configures Parallel I/O ports for CPUs with QE support.
@@ -1772,6 +1809,61 @@ platforms are moved over to use the flattened-device-tree model.
 		};
 	};
 
+  viii) Clocks (clocks)
+	This node specifies the frequency values for all the external clocks
+	viz CLK1 to CLK24 in Hz.
+
+  Required Properties:
+  - compatible : should be "fsl,cpm-clocks".
+  - #clock-cells : It specifies the number of cells occupied by clock-frequency
+    property. Currently #clock-cells = 1 is only supported and implemented.
+    This property is kept for future in case we need frequencies higher than
+    4 GHz.
+  - clock-frequency : It is a list of u32 values to represent the frequency
+    of each external clock(CLK1 to CLK24) in Hz.Each entry occupies
+    number of cells specified by #clock-cells property(1 for now).
+
+  Example:
+
+		clocks {
+			compatible = "fsl,cpm-clocks";
+			#clock-cells = <1>;
+			/* clock freqs in Hz(for CLK1~CLK24).
+			 * CLK11 is 1024KHz,
+			 * all other clocks unused
+			 */
+			clock-frequency = <0 0 0 0 0 0
+					   0 0 0 0 0 d#1024000 0
+					   0 0 0 0 0 0
+					   0 0 0 0 0 0>;
+		};
+
+  ix) Baud Rate Generator (BRG)
+
+  Required properties:
+  - compatible : shpuld be "fsl,cpm-brg"
+  - fsl,brg-sources : define the input clock for all 16 BRGs. The input
+    clock source could be 1 to 24 for CLK1 to CLK24. Zero means that the
+    particular BRG will be driven by QE clock(BRGCLK).
+  - reg : This property defines the address and size of the memory-mapped
+    registers of the BRG.
+
+  Example:
+
+		brg@640 {
+			compatible = "fsl,qe-brg";
+			/* input clock sources for all the 16 BRGs.
+			 * 1-24 for CLK1 to CLK24.
+			 * BRG9 uses CLK11 others use
+			 * the QE clock.
+			 */
+			fsl,brg-sources = <0 0 0 0 0 0 0 0
+					   b 0 0 0 0 0 0 0>;
+			reg = <640 7f>;
+		};
+
+    In the above entry, for BRG9 the input clock is 0xb(decimal 11) ie QE_CLK11.
+
    j) CFI or JEDEC memory-mapped NOR flash
 
     Flash chips (Memory Technology Devices) are often used for solid state
-- 
1.5.2.4

^ permalink raw reply related

* [PATCH 2/3] arch/ : Platform changes for UCC TDM driver for MPC8323ERDB.Also includes related QE changes.
From: Poonam_Aggrwal-b10812 @ 2007-12-10 12:09 UTC (permalink / raw)
  To: rubini, linux-kernel, netdev, kumar.gala, linuxppc-dev
  Cc: michael.barkowski, rich.cutler, ashish.kalra

From: Poonam Aggrwal <b10812@freescale.com>

This patch makes necessary changes in the QE and UCC framework to support 
TDM. It also adds support to configure the BRG properly through device 
tree entries. Includes the device tree changes for UCC TDM driver as well.
It also includes device tree entries for UCC TDM driver.

Tested on MPC8323ERDB platform.

Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
 arch/powerpc/boot/dts/mpc832x_rdb.dts |   58 +++++++
 arch/powerpc/sysdev/qe_lib/qe.c       |  128 ++++++++++++++--
 arch/powerpc/sysdev/qe_lib/ucc.c      |  265 +++++++++++++++++++++++++++++++++
 arch/powerpc/sysdev/qe_lib/ucc_fast.c |   37 +++++
 include/asm-powerpc/qe.h              |    8 +
 include/asm-powerpc/ucc.h             |    4 +
 include/asm-powerpc/ucc_fast.h        |    4 +
 7 files changed, 492 insertions(+), 12 deletions(-)

diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts
index 388c8a7..333408c 100644
--- a/arch/powerpc/boot/dts/mpc832x_rdb.dts
+++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts
@@ -105,6 +105,17 @@
 			device_type = "par_io";
 			num-ports = <7>;
 
+			ucc1pio:ucc_pin@01 {
+				pio-map = <
+			/* port  pin  dir  open_drain  assignment  has_irq */
+					0  e  2  0  1  0	/* CLK11 */
+					3 16  1  0  2  0	/* BRG9 */
+					3 1b  1  0  2  0	/* BRG3 */
+					0  0  3  0  2  0	/* TDMATxD0 */
+					0  4  3  0  2  0	/* TDMARxD0 */
+					3 1b  2  0  1  0>;	/* CLK1 */
+			};
+
 			ucc2pio:ucc_pin@02 {
 				pio-map = <
 			/* port  pin  dir  open_drain  assignment  has_irq */
@@ -169,6 +180,36 @@
 			};
 		};
 
+		clocks {
+			compatible = "fsl,cpm-clocks";
+			/* clock freqs in Hz(for CLK1~CLK24).
+			 * CLK11 is 1024KHz,
+			 * all other clocks unused
+			 * #clock-cells define number of cells
+			 * used by the clock-frequency.
+			 * right now only #clock cells=1 is
+			 * implemented. Provision is there to
+			 * handle frequencies >4Gig
+			 */
+			#clock-cells = <1>;
+			clock-frequency = <0 0 0 0 0 0
+					   0 0 0 0 d#1024000 0
+					   0 0 0 0 0 0
+					   0 0 0 0 0 0>;
+		};
+
+		brg@640 {
+			compatible = "fsl,cpm-brg";
+			/* input clock sources for all the 16 BRGs.
+			 * 1-24 for CLK1 to CLK24.
+			 * BRG9 uses CLK11,BRG1 and BRG2-8 use
+			 * the QE clock.
+			 */
+			fsl,brg-sources = <0 0 0 0 0 0 0 0
+					   b 0 0 0 0 0 0 0>;
+			reg = <640 7f>;
+		};
+
 		spi@4c0 {
 			device_type = "spi";
 			compatible = "fsl_spi";
@@ -187,6 +228,23 @@
 			mode = "cpu";
 		};
 
+		ucc@2000 {
+			device_type = "tdm";
+			compatible = "fsl,ucc-tdm";
+			model = "UCC";
+			device-id = <1>;
+			fsl,tdm-num = <1>;
+			fsl,si-num = <1>;
+			fsl,tdm-tx-clk = "CLK1";
+			fsl,tdm-rx-clk = "CLK1";
+			fsl,tdm-tx-sync = "BRG9";
+			fsl,tdm-rx-sync = "BRG9";
+			reg = <2000 200>;
+			interrupts = <20>;
+			interrupt-parent = <&qeic>;
+			pio-handle = <&ucc1pio>;
+		};
+
 		ucc@3000 {
 			device_type = "network";
 			compatible = "ucc_geth";
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index 1df3b4a..abcf0b4 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -149,22 +149,116 @@ EXPORT_SYMBOL(qe_issue_cmd);
  */
 static unsigned int brg_clk = 0;
 
-unsigned int get_brg_clk(void)
+u32 get_brg_clk(enum qe_clock brgclk, enum qe_clock *brg_source)
 {
-	struct device_node *qe;
-	if (brg_clk)
-		return brg_clk;
+	struct device_node *qe, *brg, *clocks;
+	enum qe_clock brg_src;
+	u32 brg_input_freq = 0;
+	u32 brg_num;
+	const unsigned int *prop;
 
-	qe = of_find_node_by_type(NULL, "qe");
-	if (qe) {
+	*brg_source = 0;
+
+	brg_num = brgclk - QE_BRG1;
+	brg = of_find_compatible_node(NULL, NULL, "fsl,cpm-brg");
+	if (brg) {
 		unsigned int size;
-		const u32 *prop = of_get_property(qe, "brg-frequency", &size);
-		brg_clk = *prop;
-		of_node_put(qe);
-	};
+		prop = of_get_property(brg,
+					"fsl,brg-sources", &size);
+
+		brg_src = *(prop + brg_num);
+		if (brg_src == 0) {
+			*brg_source = 0;
+			if (brg_clk > 0) {
+				of_node_put(brg);
+				return brg_clk;
+			}
+			qe = of_find_node_by_type(NULL, "qe");
+			if (qe) {
+				unsigned int size;
+				prop = of_get_property
+						(qe, "brg-frequency", &size);
+				of_node_put(qe);
+				of_node_put(brg);
+				return *prop;
+			}
+		} else {
+			*brg_source = brg_src + QE_CLK1 - 1;
+			clocks = of_find_compatible_node(NULL, NULL,
+							"fsl,cpm-clocks");
+			prop = of_get_property(clocks,
+						"#clock-cells", &size);
+			/*
+			 * clock-cells = 1 only supported right now.
+			 */
+			if (*prop != 1)
+				return 0;
+			prop = of_get_property(clocks,
+						"clock-frequency", &size);
+
+			brg_input_freq = *(prop+(brg_src - 1));
+			of_node_put(clocks);
+			of_node_put(brg);
+			return brg_input_freq;
+		}
+	}
 	return brg_clk;
 }
 
+u32 qe_brg_src(int brg_num, enum qe_clock brg_src)
+{
+	u32 clock_bits, shift;
+
+	clock_bits = 0;
+
+	switch (brg_num) {
+	case 1:
+	case 2:
+	case 5:
+	case 6:
+		switch (brg_src) {
+		case QE_CLK3:   clock_bits = 1; break;
+		case QE_CLK5:   clock_bits = 2; break;
+		default: break;
+		}
+		break;
+	case 3:
+	case 4:
+	case 7:
+	case 8:
+		switch (brg_src) {
+		case QE_CLK9:    clock_bits = 1; break;
+		case QE_CLK15:   clock_bits = 2; break;
+		default: break;
+		}
+		break;
+	case 9:
+	case 10:
+		switch (brg_src) {
+		case QE_CLK11:    clock_bits = 1; break;
+		default: break;
+		}
+		break;
+	case 11:
+	case 15:
+	case 16:
+		switch (brg_src) {
+		case QE_CLK13:    clock_bits = 1; break;
+		default: break;
+		}
+		break;
+	default: clock_bits = 0; break;
+	}
+	shift = 14;
+
+	if (!clock_bits)
+		return -ENOENT;
+
+	clock_bits <<= shift;
+
+	return clock_bits;
+}
+
 /* Program the BRG to the given sampling rate and multiplier
  *
  * @brg: the BRG, QE_BRG1 - QE_BRG16
@@ -177,11 +271,18 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
 {
 	u32 divisor, tempval;
 	u32 div16 = 0;
+	u32 brg_clock;
+	enum qe_clock brgsrc;
+	u32 src_bits = 0;
 
 	if ((brg < QE_BRG1) || (brg > QE_BRG16))
 		return -EINVAL;
 
-	divisor = get_brg_clk() / (rate * multiplier);
+	brg_clock = get_brg_clk(brg, &brgsrc);
+	if (!brg_clock)
+		return -EINVAL;
+
+	divisor = brg_clock / (rate * multiplier);
 
 	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
 		div16 = QE_BRGC_DIV16;
@@ -194,8 +295,11 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
 	if (!div16 && (divisor & 1))
 		divisor++;
 
+	if (brgsrc > 0)
+		src_bits = qe_brg_src(brg - QE_BRG1 + 1, brgsrc);
+
 	tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) |
-		QE_BRGC_ENABLE | div16;
+		QE_BRGC_ENABLE | div16 | src_bits;
 
 	out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval);
 
diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c
index 0e348d9..f2de0ed 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc.c
@@ -213,3 +213,268 @@ int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
 
 	return 0;
 }
+
+int ucc_set_tdm_rxtx_clk(int tdm_num, char *clk_src, enum comm_dir mode)
+{
+	enum qe_clock clock;
+	u32 clock_bits, shift;
+	struct qe_mux *qe_mux_reg = NULL;
+
+	clock_bits = 0;
+	qe_mux_reg = &qe_immr->qmx;
+
+	if ((tdm_num > 3 || tdm_num < 0))
+		return -EINVAL;
+
+	/* The communications direction must be RX or TX */
+	if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX)))
+		return -EINVAL;
+
+	clock = qe_clock_source(clk_src);
+	switch (mode) {
+	case COMM_DIR_RX:
+		switch (tdm_num) {
+		case 0:
+			switch (clock) {
+			case QE_BRG3:   clock_bits = 1; break;
+			case QE_BRG4:   clock_bits = 2; break;
+			case QE_CLK1:   clock_bits = 4; break;
+			case QE_CLK2:   clock_bits = 5; break;
+			case QE_CLK3:   clock_bits = 6; break;
+			case QE_CLK8:   clock_bits = 7; break;
+			default: break;
+			}
+			shift = 28;
+			break;
+		case 1:
+			switch (clock) {
+			case QE_BRG3:   clock_bits = 1; break;
+			case QE_BRG4:   clock_bits = 2; break;
+			case QE_CLK1:   clock_bits = 4; break;
+			case QE_CLK2:   clock_bits = 5; break;
+			case QE_CLK5:   clock_bits = 6; break;
+			case QE_CLK10:  clock_bits = 7; break;
+			default: break;
+			}
+			shift = 24;
+			break;
+		case 2:
+			switch (clock) {
+			case QE_BRG3:   clock_bits = 1; break;
+			case QE_BRG4:   clock_bits = 2; break;
+			case QE_CLK1:   clock_bits = 4; break;
+			case QE_CLK2:   clock_bits = 5; break;
+			case QE_CLK7:   clock_bits = 6; break;
+			case QE_CLK12:  clock_bits = 7; break;
+			default: break;
+			}
+			shift = 20;
+			break;
+		case 3:
+			switch (clock) {
+			case QE_BRG3:   clock_bits = 1; break;
+			case QE_BRG4:   clock_bits = 2; break;
+			case QE_CLK1:   clock_bits = 4; break;
+			case QE_CLK2:   clock_bits = 5; break;
+			case QE_CLK9:   clock_bits = 6; break;
+			case QE_CLK14:  clock_bits = 7; break;
+			default: break;
+			}
+			shift = 16;
+			break;
+		default:
+			break;
+		}
+		break;
+	case COMM_DIR_TX:
+		switch (tdm_num) {
+		case 0:
+			switch (clock) {
+			case QE_BRG3:   clock_bits = 1; break;
+			case QE_BRG4:   clock_bits = 2; break;
+			case QE_CLK1:   clock_bits = 4; break;
+			case QE_CLK2:   clock_bits = 5; break;
+			case QE_CLK4:   clock_bits = 6; break;
+			case QE_CLK9:   clock_bits = 7; break;
+			default: break;
+			}
+			shift = 12;
+			break;
+		case 1:
+			switch (clock) {
+			case QE_BRG3:   clock_bits = 1; break;
+			case QE_BRG4:   clock_bits = 2; break;
+			case QE_CLK1:   clock_bits = 4; break;
+			case QE_CLK2:   clock_bits = 5; break;
+			case QE_CLK6:   clock_bits = 6; break;
+			case QE_CLK11:  clock_bits = 7; break;
+			default: break;
+			}
+			shift = 8;
+			break;
+		case 2:
+			switch (clock) {
+			case QE_BRG3:   clock_bits = 1; break;
+			case QE_BRG4:   clock_bits = 2; break;
+			case QE_CLK1:   clock_bits = 4; break;
+			case QE_CLK2:   clock_bits = 5; break;
+			case QE_CLK8:   clock_bits = 6; break;
+			case QE_CLK13:  clock_bits = 7; break;
+			default: break;
+			}
+			shift = 4;
+			break;
+		case 3:
+			switch (clock) {
+			case QE_BRG3:   clock_bits = 1; break;
+			case QE_BRG4:   clock_bits = 2; break;
+			case QE_CLK1:   clock_bits = 4; break;
+			case QE_CLK2:   clock_bits = 5; break;
+			case QE_CLK10:  clock_bits = 6; break;
+			case QE_CLK15:  clock_bits = 7; break;
+			default: break;
+			}
+			shift = 0;
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (!clock_bits)
+		return -ENOENT;
+
+	clock_bits <<= shift;
+
+	qe_mux_reg->cmxsi1cr_l |= clock_bits;
+
+	return 0;
+}
+
+int ucc_set_tdm_rxtx_sync(int tdm_num, char *sync_src, enum comm_dir mode)
+{
+	enum qe_clock clock;
+	u32 shift, clock_bits;
+	struct qe_mux *qe_mux_reg = NULL;
+	int source;
+
+	source = -1;
+	qe_mux_reg = &qe_immr->qmx;
+
+	if ((tdm_num > 3 || tdm_num < 0))
+		return -EINVAL;
+
+	/* The communications direction must be RX or TX */
+	if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX)))
+		return -EINVAL;
+
+	switch (mode) {
+	case COMM_DIR_RX:
+		if (strcasecmp("RSYNC", sync_src) == 0) {
+			source = 0;
+			shift = 0;
+			break;
+		}
+		clock = qe_clock_source(sync_src);
+		switch (tdm_num) {
+		case 0:
+			switch (clock) {
+			case QE_BRG9:   source = 1; break;
+			case QE_BRG10:  source = 2; break;
+			default: source = -1; break;
+			}
+			shift = 30;
+			break;
+		case 1:
+			switch (clock) {
+			case QE_BRG9:   source = 1; break;
+			case QE_BRG10:  source = 2; break;
+			default: source = -1; break;
+			}
+			shift = 28;
+			break;
+		case 2:
+			switch (clock) {
+			case QE_BRG9:   source = 1; break;
+			case QE_BRG11:  source = 2; break;
+			default: source = -1; break;
+			}
+			shift = 26;
+			break;
+		case 3:
+			switch (clock) {
+			case QE_BRG9:   source = 1; break;
+			case QE_BRG11:  source = 2; break;
+			default: source = -1; break;
+			}
+			shift = 24;
+			break;
+		default:
+			source = -1;
+			break;
+		}
+		break;
+	case COMM_DIR_TX:
+		if (strcasecmp("TSYNC", sync_src) == 0) {
+			source = 0;
+			shift = 0;
+			break;
+		}
+		clock = qe_clock_source(sync_src);
+		switch (tdm_num) {
+		case 0:
+			switch (clock) {
+			case QE_BRG9:   source = 1; break;
+			case QE_BRG10:  source = 2; break;
+			default: source = -1; break;
+			}
+			shift = 14;
+			break;
+		case 1:
+			switch (clock) {
+			case QE_BRG9:   source = 1; break;
+			case QE_BRG10:  source = 2; break;
+			default: source = -1; break;
+			}
+			shift = 12;
+			break;
+		case 2:
+			switch (clock) {
+			case QE_BRG9:   source = 1; break;
+			case QE_BRG11:  source = 2; break;
+			default: source = -1; break;
+			}
+			shift = 10;
+			break;
+		case 3:
+			switch (clock) {
+			case QE_BRG9:   source = 1; break;
+			case QE_BRG11:  source = 2; break;
+			default: source = -1; break;
+			}
+			shift = 8;
+			break;
+		default:
+			source = -1;
+			break;
+		}
+		break;
+	default:
+		source = -1;
+		break;
+	}
+
+	if (source == -1)
+		return -ENOENT;
+
+	clock_bits = (u32) source;
+	clock_bits <<= shift;
+
+
+	qe_mux_reg->cmxsi1syr  |= clock_bits;
+
+	return 0;
+}
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
index 3223acb..9c8559f 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
@@ -327,6 +327,43 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
 			ucc_fast_free(uccf);
 			return -EINVAL;
 		}
+	} else {
+		/* TDM Rx clock routing */
+		if ((uf_info->tdm_rx_clk != NULL) &&
+			ucc_set_tdm_rxtx_clk(uf_info->ucc_num,
+				uf_info->tdm_rx_clk, COMM_DIR_RX)) {
+			printk(KERN_ERR "%s: illegal value for TDM RX clock",
+				__FUNCTION__);
+			ucc_fast_free(uccf);
+			return -EINVAL;
+		}
+		/* TDM Tx clock routing */
+		if ((uf_info->tdm_tx_clk != NULL) &&
+			ucc_set_tdm_rxtx_clk(uf_info->ucc_num,
+				uf_info->tdm_tx_clk, COMM_DIR_TX)) {
+			printk(KERN_ERR "%s: illegal value for TDM TX clock",
+				__FUNCTION__);
+			ucc_fast_free(uccf);
+			return -EINVAL;
+		}
+		/* TDM Rx sync routing */
+		if ((uf_info->tdm_rx_sync != NULL) &&
+			ucc_set_tdm_rxtx_sync(uf_info->ucc_num,
+				uf_info->tdm_rx_sync, COMM_DIR_RX)) {
+			printk(KERN_ERR "%s: illegal value for TDM RX"
+				"Frame sync", __FUNCTION__);
+			ucc_fast_free(uccf);
+			return -EINVAL;
+		}
+		/* TDM Tx sync routing */
+		if ((uf_info->tdm_tx_sync != NULL) &&
+			ucc_set_tdm_rxtx_sync(uf_info->ucc_num,
+				uf_info->tdm_tx_sync, COMM_DIR_TX)) {
+			printk(KERN_ERR "%s: illegal value for TDM TX"
+					"Frame sync", __FUNCTION__);
+			ucc_fast_free(uccf);
+			return -EINVAL;
+		}
 	}
 
 	/* Set interrupt mask register at UCC level. */
diff --git a/include/asm-powerpc/qe.h b/include/asm-powerpc/qe.h
index bcf60be..51de236 100644
--- a/include/asm-powerpc/qe.h
+++ b/include/asm-powerpc/qe.h
@@ -497,6 +497,14 @@ struct ucc_slow_pram {
 #define UCC_GETH_UCCE_RXF1      0x00000002
 #define UCC_GETH_UCCE_RXF0      0x00000001
 
+/* Transparent UCC Event Register (UCCE) */
+#define UCC_TRANS_UCCE_GRA	0x0080
+#define UCC_TRANS_UCCE_TXE	0x0010
+#define UCC_TRANS_UCCE_RXF	0x0008
+#define UCC_TRANS_UCCE_BSY	0x0004
+#define UCC_TRANS_UCCE_TXB	0x0002
+#define UCC_TRANS_UCCE_RXB	0x0001
+
 /* UPSMR, when used as a UART */
 #define UCC_UART_UPSMR_FLC		0x8000
 #define UCC_UART_UPSMR_SL		0x4000
diff --git a/include/asm-powerpc/ucc.h b/include/asm-powerpc/ucc.h
index 46b09ba..153db97 100644
--- a/include/asm-powerpc/ucc.h
+++ b/include/asm-powerpc/ucc.h
@@ -42,6 +42,10 @@ int ucc_set_qe_mux_mii_mng(unsigned int ucc_num);
 int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
 	enum comm_dir mode);
 
+int ucc_set_tdm_rxtx_clk(int tdm_num, char *clk_src, enum comm_dir mode);
+
+int ucc_set_tdm_rxtx_sync(int tdm_num, char *clk_src, enum comm_dir mode);
+
 int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask);
 
 /* QE MUX clock routing for UCC
diff --git a/include/asm-powerpc/ucc_fast.h b/include/asm-powerpc/ucc_fast.h
index f529f70..d267983 100644
--- a/include/asm-powerpc/ucc_fast.h
+++ b/include/asm-powerpc/ucc_fast.h
@@ -152,6 +152,10 @@ struct ucc_fast_info {
 	enum ucc_fast_rx_decoding_method renc;
 	enum ucc_fast_transparent_tcrc tcrc;
 	enum ucc_fast_sync_len synl;
+	char *tdm_rx_clk;
+	char *tdm_tx_clk;
+	char *tdm_rx_sync;
+	char *tdm_tx_sync;
 };
 
 struct ucc_fast_private {
-- 
1.5.2.4

^ permalink raw reply related

* [PATCH 1/3] drivers/misc :UCC based TDM driver for MPC83xx platforms.
From: Poonam_Aggrwal-b10812 @ 2007-12-10 12:04 UTC (permalink / raw)
  To: rubini, linux-kernel, linuxppc-dev, netdev, kumar.gala
  Cc: michael.barkowski, rich.cutler, ashish.kalra

From: Poonam Aggrwal <b10812@freescale.com>

The UCC TDM driver basically multiplexes and demultiplexes data from 
different channels. It can interface with for example SLIC kind of devices 
to receive TDM data  demultiplex it and send to upper applications. At the 
transmit end it receives data for different channels multiplexes it and 
sends them on the TDM channel. It internally uses TSA( Time Slot Assigner) 
which does multiplexing and demultiplexing, UCC to perform SDMA between 
host buffers and the TSA, CMX to connect TSA to UCC.

This driver will run on MPC8323E-RDB platforms.


Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
 drivers/misc/Kconfig   |   21 +
 drivers/misc/Makefile  |    1 +
 drivers/misc/ucc_tdm.c | 1068 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/misc/ucc_tdm.h |  227 ++++++++++
 4 files changed, 1317 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/ucc_tdm.c
 create mode 100644 drivers/misc/ucc_tdm.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b5e67c0..698a72c 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -219,6 +219,27 @@ config THINKPAD_ACPI_BAY
 
 	  If you are not sure, say Y here.
 
+config UCC_TDM
+	bool "Freescale UCC  TDM Driver"
+	depends on QUICC_ENGINE && UCC_FAST
+	default n
+	---help---
+	  The TDM driver is for UCC based TDM devices for example, TDM device on
+	  MPC832x RDB. Select it to run PowerVoIP on MPC832x RDB board.
+	  The TDM driver can interface with SLIC kind of devices to transmit
+	  and receive TDM samples. The TDM driver receives Time Division
+	  multiplexed samples(for different channels) from the SLIC device,
+	  demutiplexes them and sends them to the upper layers. At the transmit
+	  end the TDM drivers receives samples for different channels, it
+	  multiplexes them and sends them to the SLIC device.
+
+config TDM_LINEAR_PCM
+	bool "Linear PCM mode"
+	depends on UCC_TDM
+	---help---
+	  This mode should be selected if the TDM driver interface with the
+	  SLIC device is linear PCM(e.g. 16 bit samples). If not selected the
+	  interface will be 8 bit u-law.
 
 config ATMEL_SSC
 	tristate "Device driver for Atmel SSC peripheral"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 87f2685..6f0c49d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SONY_LAPTOP)	+= sony-laptop.o
 obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
 obj-$(CONFIG_FUJITSU_LAPTOP)	+= fujitsu-laptop.o
 obj-$(CONFIG_EEPROM_93CX6)	+= eeprom_93cx6.o
+obj-$(CONFIG_UCC_TDM)		+= ucc_tdm.o
diff --git a/drivers/misc/ucc_tdm.c b/drivers/misc/ucc_tdm.c
new file mode 100644
index 0000000..232d537
--- /dev/null
+++ b/drivers/misc/ucc_tdm.c
@@ -0,0 +1,1068 @@
+/*
+ * drivers/misc/ucc_tdm.c
+ *
+ * UCC Based Linux TDM Driver
+ * This driver is designed to support UCC based TDM for PowerPC processors.
+ * This driver can interface with SLIC device to run VOIP kind of
+ * applications.
+ *
+ * Author: Ashish Kalra & Poonam Aggrwal
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+#include <linux/irq.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+
+#include <asm/immap_qe.h>
+#include <asm/qe.h>
+#include <asm/ucc.h>
+#include <asm/ucc_fast.h>
+#include <asm/ucc_slow.h>
+
+#include "ucc_tdm.h"
+#define DRV_DESC "Freescale QE UCC TDM Driver"
+#define DRV_NAME "ucc_tdm"
+
+/*
+ * define the following #define if snooping or hardware-based cache coherency
+ * is disabled on the UCC transparent controller.This flag enables
+ * software-based cache-coherency support by explicitly flushing data cache
+ * contents after setting up the TDM output buffer(s) and invalidating the
+ * data cache contents 	before the TDM input buffer(s) are read.
+ */
+#undef UCC_CACHE_SNOOPING_DISABLED
+
+#define MAX_NUM_TDM_DEVICES 8
+
+static struct tdm_ctrl *tdm_ctrl[MAX_NUM_TDM_DEVICES];
+
+static int num_tdm_devices;
+static int num_tdm_clients;
+
+#define PREV_PHASE(x) ((x == 0) ? MAX_PHASE : (x - 1))
+#define NEXT_PHASE(x) (((x + 1) > MAX_PHASE) ? 0 : (x + 1))
+
+static struct ucc_tdm_info utdm_primary_info = {
+	.uf_info = {
+		.tsa = 1,
+		.cdp = 1,
+		.cds = 1,
+		.ctsp = 1,
+		.ctss = 1,
+		.revd = 1,
+		.urfs = 0x128,
+		.utfs = 0x128,
+		.utfet = 0,
+		.utftt = 0x128,
+		.ufpt = 256,
+		.ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_TRANSPARENT,
+		.tenc = UCC_FAST_TX_ENCODING_NRZ,
+		.renc = UCC_FAST_RX_ENCODING_NRZ,
+		.tcrc = UCC_FAST_16_BIT_CRC,
+		.synl = UCC_FAST_SYNC_LEN_NOT_USED,
+	},
+	.ucc_busy = 0,
+};
+
+static struct ucc_tdm_info utdm_info[8];
+
+static void dump_siram(struct tdm_ctrl *tdm_c)
+{
+#if defined(DEBUG)
+	int i;
+	u16 phy_num_ts;
+
+	phy_num_ts = tdm_c->physical_num_ts;
+
+	pr_debug("SI TxRAM dump\n");
+	/* each slot entry in SI RAM is of 2 bytes */
+	for (i = 0; i < phy_num_ts * 2; i++)
+		pr_debug("%x ", in_8(&qe_immr->sir.tx[i]));
+	pr_debug("\nSI RxRAM dump\n");
+	for (i = 0; i < phy_num_ts * 2; i++)
+		pr_debug("%x ", in_8(&qe_immr->sir.rx[i]));
+	pr_debug("\n");
+#endif
+}
+
+static void dump_ucc(struct tdm_ctrl *tdm_c)
+{
+#if defined(DEBUG)
+	struct ucc_transparent_pram *ucc_pram;
+
+	ucc_pram = tdm_c->ucc_pram;
+
+	pr_debug("%s Dumping UCC Registers\n", __FUNCTION__);
+	ucc_fast_dump_regs(tdm_c->uf_private);
+	pr_debug("%s Dumping UCC Parameter RAM\n", __FUNCTION__);
+	pr_debug("rbase = 0x%x\n", in_be32(&ucc_pram->rbase));
+	pr_debug("rbptr = 0x%x\n", in_be32(&ucc_pram->rbptr));
+	pr_debug("mrblr = 0x%x\n", in_be16(&ucc_pram->mrblr));
+	pr_debug("rbdlen = 0x%x\n", in_be16(&ucc_pram->rbdlen));
+	pr_debug("rbdstat = 0x%x\n", in_be16(&ucc_pram->rbdstat));
+	pr_debug("rstate = 0x%x\n", in_be32(&ucc_pram->rstate));
+	pr_debug("rdptr = 0x%x\n", in_be32(&ucc_pram->rdptr));
+	pr_debug("tbase = 0x%x\n", in_be32(&ucc_pram->tbase));
+	pr_debug("tbptr = 0x%x\n", in_be32(&ucc_pram->tbptr));
+	pr_debug("tbdlen = 0x%x\n", in_be16(&ucc_pram->tbdlen));
+	pr_debug("tbdstat = 0x%x\n", in_be16(&ucc_pram->tbdstat));
+	pr_debug("tstate = 0x%x\n", in_be32(&ucc_pram->tstate));
+	pr_debug("tdptr = 0x%x\n", in_be32(&ucc_pram->tdptr));
+#endif
+}
+/*
+ * converts u-law compressed samples to linear PCM
+ * If the CONFIG_TDM_LINEAR_PCM flag is not set the
+ * TDM driver receives u-law compressed data from the
+ * SLIC device. This function converts the compressed
+ * data to linear PCM and sends it to upper layers.
+ */
+static inline int ulaw2int(unsigned char log)
+{
+	u32 sign, segment, temp, quant;
+	int val;
+
+	temp = log ^ 0xFF;
+	sign = (temp & 0x80) >> 7;
+	segment = (temp & 0x70) >> 4;
+	quant = temp & 0x0F;
+	quant <<= 1;
+	quant += 33;
+	quant <<= segment;
+	if (sign)
+		val = 33 - quant;
+	else
+		val = quant - 33;
+
+	val *= 4;
+	return val;
+}
+
+/*
+ * converts linear PCM samples to u-law compressed format.
+ * If the CONFIG_TDM_LINEAR_PCM flag is not set the
+ * TDM driver calls this function to convert the PCM samples
+ * to u-law compressed format before sending them to SLIC
+ * device.
+ */
+static inline u8 int2ulaw(short linear)
+{
+	u8  quant, ret;
+	u16 output, absol, temp;
+	u32 i, sign;
+	char segment;
+
+	ret = 0;
+	if (linear >= 0)
+		linear = (linear >> 2);
+	else
+		linear = (0xc000 | (linear >> 2));
+
+	absol = abs(linear) + 33;
+	temp = absol;
+	sign = (linear >= 0) ? 1 : 0;
+	for (i = 0; i < 16; i++) {
+		output = temp & 0x8000;
+		if (output)
+			break;
+		temp <<= 1;
+	}
+	segment = 11 - i;
+	quant = (absol >> segment) & 0x0F;
+	segment--;
+	segment <<= 4;
+	output = segment + quant;
+	if (absol > 8191)
+		output = 0x7F;
+	if (sign)
+		ret ^= 0xFF;
+	else
+		ret ^= 0x7F;
+	return ret;
+}
+
+/*
+ * For use when a framing bit is not present
+ * Program current-route SI ram
+ * Set SIxRAM TDMx
+ * Entries must be in units of 8.
+ * SIR_UCC -> Channel Select
+ * SIR_CNT -> Number of bits or bytes
+ * SIR_BYTE -> Byte or Bit resolution
+ * SIR_LAST -> Indicates last entry in SIxRAM
+ * SIR_IDLE -> The Tx data pin is Tri-stated and the Rx data pin is
+ * 		ignored
+ */
+static void set_siram(struct tdm_ctrl *tdm_c, enum comm_dir dir)
+{
+	const u16 *mask;
+	u16 temp_mask = 1;
+	u16 siram_code = 0;
+	u32 i, j, k;
+	u32 ucc;
+	u32 phy_num_ts;
+
+	phy_num_ts = tdm_c->physical_num_ts;
+	ucc = tdm_c->ut_info->uf_info.ucc_num;
+
+	if (dir == COMM_DIR_RX)
+		mask = tdm_c->rx_mask;
+	else
+		mask = tdm_c->tx_mask;
+	k = 0;
+	j = 0;
+	for (i = 0; i < phy_num_ts; i++) {
+		if ((mask[k] & temp_mask) == temp_mask)
+			siram_code = SIR_UCC(ucc) | SIR_CNT(0) | SIR_BYTE;
+		else
+			siram_code = SIR_IDLE | SIR_CNT(0) | SIR_BYTE;
+		if (dir == COMM_DIR_RX)
+			SET_RX_SI_RAM(i, siram_code);
+		else
+			SET_TX_SI_RAM(i, siram_code);
+		temp_mask = temp_mask << 1;
+		j++;
+		if (j >= 16) {
+			j = 0;
+			temp_mask = 0x0001;
+			k++;
+		}
+	}
+	siram_code = siram_code | SIR_LAST;
+
+	if (dir == COMM_DIR_RX)
+		SET_RX_SI_RAM(phy_num_ts - 1, siram_code);
+	else
+		SET_TX_SI_RAM(phy_num_ts - 1, siram_code);
+}
+
+static void config_si(struct tdm_ctrl *tdm_c)
+{
+	u8 rxsyncdelay, txsyncdelay, tdm_port;
+	u16 sixmr_val = 0;
+	u32 tdma_mode_off;
+	u16 *si1_tdm_mode_reg;
+
+	tdm_port = tdm_c->tdm_port;
+
+	set_siram(tdm_c, COMM_DIR_RX);
+
+	set_siram(tdm_c, COMM_DIR_TX);
+
+	rxsyncdelay = tdm_c->cfg_ctrl.rx_fr_sync_delay;
+	txsyncdelay = tdm_c->cfg_ctrl.tx_fr_sync_delay;
+	if (tdm_c->cfg_ctrl.com_pin)
+		sixmr_val |= SIMODE_CRT;
+	if (tdm_c->cfg_ctrl.fr_sync_level == 1)
+		sixmr_val |= SIMODE_SL;
+	if (tdm_c->cfg_ctrl.clk_edge == 1)
+		sixmr_val |= SIMODE_CE;
+	if (tdm_c->cfg_ctrl.fr_sync_edge == 1)
+		sixmr_val |= SIMODE_FE;
+	sixmr_val |= (SIMODE_TFSD(txsyncdelay) | SIMODE_RFSD(rxsyncdelay));
+
+	tdma_mode_off = SI_TDM_MODE_REGISTER_OFFSET * tdm_c->tdm_port;
+
+	si1_tdm_mode_reg = (u8 *)&qe_immr->si1 + tdma_mode_off;
+	out_be16(si1_tdm_mode_reg, sixmr_val);
+
+	dump_siram(tdm_c);
+}
+
+static int tdm_init(struct tdm_ctrl *tdm_c)
+{
+	u32 tdm_port, ucc, act_num_ts;
+	int ret, i, err;
+	u32 cecr_subblock;
+	u32 pram_offset;
+	u32 rxbdt_offset;
+	u32 txbdt_offset;
+	u32 rx_ucode_buf_offset, tx_ucode_buf_offset;
+	u16 bd_status, bd_len;
+	enum qe_clock clock;
+	struct qe_bd __iomem *rx_bd, *tx_bd;
+
+	tdm_port = tdm_c->tdm_port;
+	ucc = tdm_c->ut_info->uf_info.ucc_num;
+	act_num_ts = tdm_c->cfg_ctrl.active_num_ts;
+
+	/*
+	 * TDM Tx and Rx CLKs = 2048 KHz.
+	 */
+	if (strstr(tdm_c->ut_info->uf_info.tdm_tx_clk, "BRG")) {
+		clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_tx_clk);
+		err = qe_setbrg(clock, 2048000, 1);
+		if (err < 0) {
+			printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
+				tdm_c->ut_info->uf_info.tdm_tx_clk);
+			return err;
+		}
+	}
+	if (strstr(tdm_c->ut_info->uf_info.tdm_rx_clk, "BRG")) {
+		clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_rx_clk);
+		err = qe_setbrg(clock, 2048000, 1);
+		if (err < 0) {
+			printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
+				tdm_c->ut_info->uf_info.tdm_rx_clk);
+			return err;
+		}
+	}
+	/*
+	 * TDM FSyncs = 4 KHz.
+	 */
+	if (strstr(tdm_c->ut_info->uf_info.tdm_tx_sync, "BRG")) {
+		clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_tx_sync);
+		err = qe_setbrg(clock, 4000, 1);
+		if (err < 0) {
+			printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
+				tdm_c->ut_info->uf_info.tdm_tx_sync);
+			return err;
+		}
+	}
+	if (strstr(tdm_c->ut_info->uf_info.tdm_rx_sync, "BRG")) {
+		clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_rx_sync);
+		err = qe_setbrg(clock, 4000, 1);
+		if (err < 0) {
+			printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
+				tdm_c->ut_info->uf_info.tdm_rx_sync);
+			return err;
+		}
+	}
+
+	tdm_c->ut_info->uf_info.uccm_mask = (u32)
+			((UCC_TRANS_UCCE_RXB | UCC_TRANS_UCCE_BSY) << 16);
+
+	if (ucc_fast_init(&(tdm_c->ut_info->uf_info), &tdm_c->uf_private)) {
+		printk(KERN_ERR "%s: Failed to init uccf\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	ucc_fast_disable(tdm_c->uf_private, COMM_DIR_RX | COMM_DIR_TX);
+
+	/* Write to QE CECR, UCCx channel to Stop Transmission */
+	cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
+	qe_issue_cmd(QE_STOP_TX, cecr_subblock,
+		(u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+	pram_offset = qe_muram_alloc(UCC_TRANSPARENT_PRAM_SIZE,
+					ALIGNMENT_OF_UCC_SLOW_PRAM);
+	if (IS_ERR_VALUE(pram_offset)) {
+		printk(KERN_ERR "%s: Cannot allocate MURAM memory for"
+			" transparent UCC\n", __FUNCTION__);
+		ret = -ENOMEM;
+		goto pram_alloc_error;
+	}
+
+	cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
+	qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock,
+			QE_CR_PROTOCOL_UNSPECIFIED, pram_offset);
+
+	tdm_c->ucc_pram = qe_muram_addr(pram_offset);
+	tdm_c->ucc_pram_offset = pram_offset;
+
+	/*
+	 * zero-out pram, this will also ensure RSTATE, TSTATE are cleared, also
+	 * DISFC & CRCEC counters will be initialized.
+	 */
+	memset(tdm_c->ucc_pram, 0, sizeof(struct ucc_transparent_pram));
+
+	/* rbase, tbase alignment is 8. */
+	rxbdt_offset = qe_muram_alloc(NR_BUFS * sizeof(struct qe_bd),
+					QE_ALIGNMENT_OF_BD);
+	if (IS_ERR_VALUE(rxbdt_offset)) {
+		printk(KERN_ERR "%s: Cannot allocate MURAM memory for RxBDs\n",
+				__FUNCTION__);
+		ret = -ENOMEM;
+		goto rxbd_alloc_error;
+	}
+	txbdt_offset = qe_muram_alloc(NR_BUFS * sizeof(struct qe_bd),
+				QE_ALIGNMENT_OF_BD);
+	if (IS_ERR_VALUE(txbdt_offset)) {
+		printk(KERN_ERR "%s: Cannot allocate MURAM memory for TxBDs\n",
+				__FUNCTION__);
+		ret = -ENOMEM;
+		goto txbd_alloc_error;
+	}
+	tdm_c->tx_bd = qe_muram_addr(txbdt_offset);
+	tdm_c->rx_bd = qe_muram_addr(rxbdt_offset);
+
+	tdm_c->tx_bd_offset = txbdt_offset;
+	tdm_c->rx_bd_offset = rxbdt_offset;
+
+	rx_bd = tdm_c->rx_bd;
+	tx_bd = tdm_c->tx_bd;
+
+	out_be32(&tdm_c->ucc_pram->rbase, (u32) immrbar_virt_to_phys(rx_bd));
+	out_be32(&tdm_c->ucc_pram->tbase, (u32) immrbar_virt_to_phys(tx_bd));
+
+	for (i = 0; i < NR_BUFS - 1; i++) {
+		bd_status = (u16) ((R_E | R_CM | R_I) >> 16);
+		bd_len = 0;
+		out_be16(&rx_bd->length, bd_len);
+		out_be16(&rx_bd->status, bd_status);
+		out_be32(&rx_bd->buf,
+			 tdm_c->dma_input_addr + i * SAMPLE_DEPTH * act_num_ts);
+		rx_bd += 1;
+
+		bd_status = (u16) ((T_R | T_CM) >> 16);
+		bd_len =  SAMPLE_DEPTH * act_num_ts;
+		out_be16(&tx_bd->length, bd_len);
+		out_be16(&tx_bd->status, bd_status);
+		out_be32(&tx_bd->buf,
+			tdm_c->dma_output_addr + i * SAMPLE_DEPTH * act_num_ts);
+		tx_bd += 1;
+	}
+
+	bd_status = (u16) ((R_E | R_CM | R_I | R_W) >> 16);
+	bd_len = 0;
+	out_be16(&rx_bd->length, bd_len);
+	out_be16(&rx_bd->status, bd_status);
+	out_be32(&rx_bd->buf,
+		 tdm_c->dma_input_addr + i * SAMPLE_DEPTH * act_num_ts);
+
+	bd_status = (u16) ((T_R | T_CM | T_W) >> 16);
+	bd_len =  SAMPLE_DEPTH * act_num_ts;
+	out_be16(&tx_bd->length, bd_len);
+	out_be16(&tx_bd->status, bd_status);
+	out_be32(&tx_bd->buf,
+		 tdm_c->dma_output_addr + i * SAMPLE_DEPTH * act_num_ts);
+
+	config_si(tdm_c);
+
+	setbits32(&qe_immr->ic.qimr, (0x80000000 >> ucc));
+
+	rx_ucode_buf_offset = qe_muram_alloc(32, 32);
+	if (IS_ERR_VALUE(rx_ucode_buf_offset)) {
+		printk(KERN_ERR "%s: Cannot allocate MURAM mem for Rx"
+			 " ucode buf\n", __FUNCTION__);
+		ret = -ENOMEM;
+		goto rxucode_buf_alloc_error;
+	}
+
+	tx_ucode_buf_offset = qe_muram_alloc(32, 32);
+	if (IS_ERR_VALUE(tx_ucode_buf_offset)) {
+		printk(KERN_ERR "%s: Cannot allocate MURAM mem for Tx"
+			 " ucode buf\n", __FUNCTION__);
+		ret = -ENOMEM;
+		goto txucode_buf_alloc_error;
+	}
+	out_be16(&tdm_c->ucc_pram->riptr, (u16) rx_ucode_buf_offset);
+	out_be16(&tdm_c->ucc_pram->tiptr, (u16) tx_ucode_buf_offset);
+
+	tdm_c->rx_ucode_buf_offset = rx_ucode_buf_offset;
+	tdm_c->tx_ucode_buf_offset = tx_ucode_buf_offset;
+
+	/*
+	 * set the receive buffer descriptor maximum size to be
+	 * SAMPLE_DEPTH * number of active RX channels
+	 */
+	out_be16(&tdm_c->ucc_pram->mrblr, (u16) SAMPLE_DEPTH * act_num_ts);
+
+	/*
+	 * enable snooping and BE byte ordering on the UCC pram's
+	 * tstate & rstate registers.
+	 */
+	out_be32(&tdm_c->ucc_pram->tstate, 0x30000000);
+	out_be32(&tdm_c->ucc_pram->rstate, 0x30000000);
+
+	/*Put UCC transparent controller into serial interface mode.  */
+	out_be32(&tdm_c->uf_regs->upsmr, 0);
+
+	/* Reset TX and RX for UCCx */
+	cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
+	qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock,
+			(u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+	return 0;
+
+txucode_buf_alloc_error:
+	qe_muram_free(rx_ucode_buf_offset);
+rxucode_buf_alloc_error:
+	qe_muram_free(txbdt_offset);
+txbd_alloc_error:
+	qe_muram_free(rxbdt_offset);
+rxbd_alloc_error:
+	qe_muram_free(pram_offset);
+pram_alloc_error:
+	ucc_fast_free(tdm_c->uf_private);
+	return ret;
+}
+
+static void tdm_deinit(struct tdm_ctrl *tdm_c)
+{
+	qe_muram_free(tdm_c->rx_ucode_buf_offset);
+	qe_muram_free(tdm_c->tx_ucode_buf_offset);
+
+	if (tdm_c->rx_bd_offset) {
+		qe_muram_free(tdm_c->rx_bd_offset);
+		tdm_c->rx_bd = NULL;
+		tdm_c->rx_bd_offset = 0;
+	}
+	if (tdm_c->tx_bd_offset) {
+		qe_muram_free(tdm_c->tx_bd_offset);
+		tdm_c->tx_bd = NULL;
+		tdm_c->tx_bd_offset = 0;
+	}
+	if (tdm_c->ucc_pram_offset) {
+		qe_muram_free(tdm_c->ucc_pram_offset);
+		tdm_c->ucc_pram = NULL;
+		tdm_c->ucc_pram_offset = 0;
+	}
+}
+
+
+static irqreturn_t tdm_isr(int irq, void *dev_id)
+{
+	u8 *input_tdm_buffer, *output_tdm_buffer;
+	u32 txb, rxb;
+	u32 ucc;
+	register u32 ucce = 0;
+	struct tdm_ctrl *tdm_c;
+	tdm_c = (struct tdm_ctrl *)dev_id;
+
+	tdm_c->tdm_icnt++;
+	ucc = tdm_c->ut_info->uf_info.ucc_num;
+	input_tdm_buffer = tdm_c->tdm_input_data;
+	output_tdm_buffer = tdm_c->tdm_output_data;
+
+	if (in_be32(tdm_c->uf_private->p_ucce) &
+						(UCC_TRANS_UCCE_BSY << 16)) {
+		out_be32(tdm_c->uf_private->p_ucce,
+					(UCC_TRANS_UCCE_BSY << 16));
+		pr_info("%s: From tdm isr busy interrupt\n",
+			__FUNCTION__);
+		dump_ucc(tdm_c);
+
+		return IRQ_HANDLED;
+	}
+
+	if (tdm_c->tdm_flag == 1) {
+		/* track phases for Rx/Tx */
+		tdm_c->phase_rx = NEXT_PHASE(tdm_c->phase_rx);
+		tdm_c->phase_tx = NEXT_PHASE(tdm_c->phase_tx);
+#if defined(CONFIG_TDM_HW_LB_TSA_SLIC)
+		{
+			u32 temp_rx, temp_tx, phase_tx, phase_rx;
+			int i;
+			phase_rx = tdm_c->phase_rx;
+			phase_tx = tdm_c->phase_tx;
+			phase_rx = PREV_PHASE(phase_rx);
+			phase_tx = PREV_PHASE(phase_tx);
+			temp_rx = phase_rx * SAMPLE_DEPTH * ACTIVE_CH;
+			temp_tx = phase_tx * SAMPLE_DEPTH * ACTIVE_CH;
+
+			/*check if loopback received data on TS0 is correct. */
+			pr_debug("%s: check if loopback received data on TS0"
+				 " is correct\n", __FUNCTION__);
+			pr_debug("%d,%d ", phase_rx, phase_tx);
+			for (i = 0; i < 8; i++)
+				pr_debug("%1d,%1d ",
+					 input_tdm_buffer[temp_rx + i],
+					 output_tdm_buffer[temp_tx + i]);
+			pr_debug("\n");
+		}
+#endif
+
+		/* schedule BH */
+		wake_up_interruptible(&tdm_c->wakeup_event);
+	} else {
+		if (tdm_c->tdm_icnt == STUTTER_INT_CNT) {
+			txb = in_be32(&tdm_c->ucc_pram->tbptr) -
+				in_be32(&tdm_c->ucc_pram->tbase);
+			rxb = in_be32(&tdm_c->ucc_pram->rbptr) -
+				in_be32(&tdm_c->ucc_pram->rbase);
+			tdm_c->phase_tx = txb / sizeof(struct qe_bd);
+			tdm_c->phase_rx = rxb / sizeof(struct qe_bd);
+
+#if defined(CONFIG_TDM_HW_LB_TSA_SLIC)
+			tdm_c->phase_tx = tdm_c->phase_rx;
+#endif
+
+			/* signal "stuttering" period is over */
+			tdm_c->tdm_flag = 1;
+
+			pr_debug("%s: stuttering period is over\n",
+				 __FUNCTION__);
+
+			if (in_be32(tdm_c->uf_private->p_ucce) &
+						 (UCC_TRANS_UCCE_TXE << 16)) {
+				u32 cecr_subblock;
+				out_be32(tdm_c->uf_private->p_ucce,
+						(UCC_TRANS_UCCE_TXE << 16));
+				pr_debug("%s: From tdm isr txe interrupt\n",
+					 __FUNCTION__);
+
+				cecr_subblock =
+					ucc_fast_get_qe_cr_subblock(ucc);
+				qe_issue_cmd(QE_RESTART_TX, cecr_subblock,
+					(u8) QE_CR_PROTOCOL_UNSPECIFIED,
+					0);
+			}
+		}
+	}
+
+	ucce = (in_be32(tdm_c->uf_private->p_ucce)
+			& in_be32(tdm_c->uf_private->p_uccm));
+
+	out_be32(tdm_c->uf_private->p_ucce, ucce);
+
+	return IRQ_HANDLED;
+}
+
+static int tdm_start(struct tdm_ctrl *tdm_c)
+{
+	if (request_irq(tdm_c->ut_info->uf_info.irq, tdm_isr,
+					0, "tdm", tdm_c)) {
+		printk(KERN_ERR "%s: request_irq for tdm_isr failed\n",
+			__FUNCTION__);
+		return -ENODEV;
+	}
+
+	ucc_fast_enable(tdm_c->uf_private, COMM_DIR_RX | COMM_DIR_TX);
+
+#if !defined(CONFIG_TDM_LINEAR_PCM)
+	pr_info("%s 8-bit u-law compressed mode active\n", __FUNCTION__);
+#else
+	pr_info("%s 16-bit linear pcm mode active with"
+		" slots 0 & 2\n", __FUNCTION__);
+#endif
+
+	dump_siram(tdm_c);
+	dump_ucc(tdm_c);
+
+	setbits8(&(qe_immr->si1.siglmr1_h), (0x1 << tdm_c->tdm_port));
+	pr_info("%s UCC based TDM enabled\n", __FUNCTION__);
+
+	return 0;
+}
+
+static void tdm_stop(struct tdm_ctrl *tdm_c)
+{
+	u32 port, si;
+	u32 ucc;
+	u32 cecr_subblock;
+
+	port = tdm_c->tdm_port;
+	si = tdm_c->si;
+	ucc = tdm_c->ut_info->uf_info.ucc_num;
+	cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
+
+	qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock,
+			(u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
+	qe_issue_cmd(QE_CLOSE_RX_BD, cecr_subblock,
+			(u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+	clrbits8(&qe_immr->si1.siglmr1_h, (0x1 << port));
+	ucc_fast_disable(tdm_c->uf_private, COMM_DIR_RX);
+	ucc_fast_disable(tdm_c->uf_private, COMM_DIR_TX);
+	free_irq(tdm_c->ut_info->uf_info.irq, tdm_c);
+}
+
+
+static void config_tdm(struct tdm_ctrl *tdm_c)
+{
+	u32 i, j, k;
+
+	j = 0;
+	k = 0;
+
+	/* Set Mask Bits */
+	for (i = 0; i < ACTIVE_CH; i++) {
+		tdm_c->tx_mask[k] |= (1 << j);
+		tdm_c->rx_mask[k] |= (1 << j);
+		j++;
+		if (j >= 16) {
+			j = 0;
+			k++;
+		}
+	}
+	/* physical number of slots in a frame */
+	tdm_c->physical_num_ts = NUM_TS;
+
+	/* common receive and transmit pins */
+	tdm_c->cfg_ctrl.com_pin = 1;
+
+	/* L1R/TSYNC active logic "1" */
+	tdm_c->cfg_ctrl.fr_sync_level = 0;
+
+	/*
+	 * TX data on rising edge of clock
+	 * RX data on falling edge
+	 */
+	tdm_c->cfg_ctrl.clk_edge = 0;
+
+	/* Frame sync sampled on falling edge */
+	tdm_c->cfg_ctrl.fr_sync_edge = 0;
+
+	/* no bit delay */
+	tdm_c->cfg_ctrl.rx_fr_sync_delay = 0;
+
+	/* no bit delay */
+	tdm_c->cfg_ctrl.tx_fr_sync_delay = 0;
+
+#if !defined(CONFIG_TDM_HW_LB_TSA_SLIC)
+	if (tdm_c->leg_slic) {
+		/* Need 1 bit delay for Legrity SLIC */
+		tdm_c->cfg_ctrl.rx_fr_sync_delay = 1;
+		tdm_c->cfg_ctrl.tx_fr_sync_delay = 1;
+		pr_info("%s Delay for Legerity!\n", __FUNCTION__);
+	}
+#endif
+
+	tdm_c->cfg_ctrl.active_num_ts = ACTIVE_CH;
+}
+
+static void tdm_read(u32 driver_handle, short chn_id, short *pcm_buffer,
+								short len)
+{
+	int i;
+	u32 phase_rx;
+	/* point to where to start for the current phase data processing */
+	u32 temp_rx;
+
+	struct tdm_ctrl *tdm_c = (struct tdm_ctrl *)(driver_handle);
+
+#if !defined(CONFIG_TDM_LINEAR_PCM)
+	u8 *input_tdm_buffer = tdm_c->tdm_input_data;
+
+#else
+	u16 *input_tdm_buffer =
+		(u16 *)tdm_c->tdm_input_data;
+
+#endif
+	phase_rx = tdm_c->phase_rx;
+	phase_rx = PREV_PHASE(phase_rx);
+
+	temp_rx = phase_rx * SAMPLE_DEPTH * EFF_ACTIVE_CH;
+
+#if defined(UCC_CACHE_SNOOPING_DISABLED)
+	flush_dcache_range((size_t) &input_tdm_buffer[temp_rx],
+				(size_t) &input_tdm_buffer[temp_rx +
+						SAMPLE_DEPTH * ACTIVE_CH]);
+#endif
+	for (i = 0; i < len; i++) {
+#if !defined(CONFIG_TDM_LINEAR_PCM)
+		pcm_buffer[i] =
+			ulaw2int(input_tdm_buffer[i * EFF_ACTIVE_CH +
+						temp_rx + chn_id]);
+#else
+		pcm_buffer[i] =
+			input_tdm_buffer[i * EFF_ACTIVE_CH + temp_rx + chn_id];
+#endif
+
+	}
+
+}
+
+static void tdm_write(u32 driver_handle, short chn_id, short *pcm_buffer,
+								short len)
+{
+	int i;
+	int phase_tx;
+	u32 txb;
+	/* point to where to start for the current phase data processing */
+	int temp_tx;
+	struct tdm_ctrl *tdm_c = (struct tdm_ctrl *)(driver_handle);
+
+#if !defined(CONFIG_TDM_LINEAR_PCM)
+	u8 *output_tdm_buffer;
+	output_tdm_buffer = tdm_c->tdm_output_data;
+#else
+	u16 *output_tdm_buffer;
+	output_tdm_buffer = (u16 *)tdm_c->tdm_output_data;
+#endif
+	txb = in_be32(&tdm_c->ucc_pram->tbptr) -
+			in_be32(&tdm_c->ucc_pram->tbase);
+	phase_tx = txb / sizeof(struct qe_bd);
+
+	phase_tx = PREV_PHASE(phase_tx);
+
+	temp_tx = phase_tx * SAMPLE_DEPTH * EFF_ACTIVE_CH;
+
+	for (i = 0; i < len; i++) {
+#if !defined(CONFIG_TDM_LINEAR_PCM)
+		output_tdm_buffer[i * EFF_ACTIVE_CH + temp_tx + chn_id] =
+			int2ulaw(pcm_buffer[i]);
+#else
+		output_tdm_buffer[i * EFF_ACTIVE_CH + temp_tx + chn_id] =
+			pcm_buffer[i];
+#endif
+	}
+
+#if defined(UCC_CACHE_SNOOPING_DISABLED)
+	flush_dcache_range((size_t) &output_tdm_buffer[temp_tx],
+		(size_t) &output_tdm_buffer[temp_tx + SAMPLE_DEPTH *
+							ACTIVE_CH]);
+#endif
+}
+
+
+static int tdm_register_client(struct tdm_client *tdm_client)
+{
+	if (num_tdm_clients == num_tdm_devices) {
+		printk(KERN_ERR "all TDM devices busy\n");
+		return -EBUSY;
+	}
+
+	num_tdm_clients++;
+	tdm_client->driver_handle = (u32)(tdm_ctrl[num_tdm_clients - 1]);
+	tdm_client->tdm_read = tdm_read;
+	tdm_client->tdm_write = tdm_write;
+	tdm_client->wakeup_event =
+			&(tdm_ctrl[num_tdm_clients - 1]->wakeup_event);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tdm_register_client);
+
+static int tdm_deregister_client(struct tdm_client *tdm_client)
+{
+	num_tdm_clients--;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tdm_deregister_client);
+
+static int ucc_tdm_probe(struct of_device *ofdev,
+			 const struct of_device_id *match)
+{
+	struct device_node *np = ofdev->node;
+	struct resource res;
+	const unsigned int *prop;
+	u32 ucc_num, device_num, err, ret = 0;
+	struct device_node *np_tmp = NULL;
+	dma_addr_t physaddr;
+	void *tdm_buff;
+	struct ucc_tdm_info *ut_info;
+
+	prop = of_get_property(np, "device-id", NULL);
+	ucc_num = *prop - 1;
+	if ((ucc_num < 0) || (ucc_num > 7))
+		return -ENODEV;
+
+	ut_info = &utdm_info[ucc_num];
+	if (ut_info == NULL) {
+		printk(KERN_ERR "additional data missing\n");
+		return -ENODEV;
+	}
+	if (ut_info->ucc_busy) {
+		printk(KERN_ERR "UCC in use by another TDM driver instance\n");
+		return -EBUSY;
+	}
+
+	ut_info->ucc_busy = 1;
+	tdm_ctrl[num_tdm_devices++] =
+		kzalloc(sizeof(struct tdm_ctrl), GFP_KERNEL);
+	if (!tdm_ctrl[num_tdm_devices - 1]) {
+		printk(KERN_ERR "%s: no memory to allocate for"
+			" tdm control structure\n", __FUNCTION__);
+		num_tdm_devices--;
+		return -ENOMEM;
+	}
+	device_num = num_tdm_devices - 1;
+
+	tdm_ctrl[device_num]->device = &ofdev->dev;
+	tdm_ctrl[device_num]->ut_info = ut_info;
+
+	tdm_ctrl[device_num]->ut_info->uf_info.ucc_num = ucc_num;
+
+	prop = of_get_property(np, "fsl,tdm-num", NULL);
+	if (prop == NULL) {
+		ret = -EINVAL;
+		goto get_property_error;
+	}
+
+	tdm_ctrl[device_num]->tdm_port = *prop - 1;
+
+	if (tdm_ctrl[device_num]->tdm_port > 3) {
+		ret = -EINVAL;
+		goto get_property_error;
+	}
+
+	prop = of_get_property(np, "fsl,si-num", NULL);
+	if (prop == NULL) {
+		ret = -EINVAL;
+		goto get_property_error;
+	}
+
+	tdm_ctrl[device_num]->si = *prop - 1;
+
+	tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_clk =
+			(char *) of_get_property(np, "fsl,tdm-tx-clk", NULL);
+	if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_clk == NULL) {
+		ret = -EINVAL;
+		goto get_property_error;
+	}
+
+	tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_clk =
+			(char *) of_get_property(np, "fsl,tdm-rx-clk", NULL);
+	if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_clk == NULL) {
+		ret = -EINVAL;
+		goto get_property_error;
+	}
+
+	tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_sync =
+			(char *) of_get_property(np, "fsl,tdm-tx-sync", NULL);
+	if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_sync == NULL) {
+		ret = -EINVAL;
+		goto get_property_error;
+	}
+
+	tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_sync =
+			(char *) of_get_property(np, "fsl,tdm-rx-sync", NULL);
+	if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_sync == NULL) {
+		ret = -EINVAL;
+		goto get_property_error;
+	}
+
+	tdm_ctrl[device_num]->ut_info->uf_info.irq =
+					irq_of_parse_and_map(np, 0);
+	err = of_address_to_resource(np, 0, &res);
+	if (err) {
+		ret = EINVAL;
+		goto get_property_error;
+	}
+	tdm_ctrl[device_num]->ut_info->uf_info.regs = res.start;
+	tdm_ctrl[device_num]->uf_regs = of_iomap(np, 0);
+
+	np_tmp = of_find_compatible_node(np_tmp, "slic", "legerity-slic");
+	if (np_tmp != NULL)
+		tdm_ctrl[device_num]->leg_slic = 1;
+	else
+		tdm_ctrl[device_num]->leg_slic = 0;
+
+	config_tdm(tdm_ctrl[device_num]);
+
+	tdm_buff = dma_alloc_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
+				tdm_ctrl[device_num]->cfg_ctrl.active_num_ts,
+					&physaddr, GFP_KERNEL);
+	if (!tdm_buff) {
+		printk(KERN_ERR "ucc-tdm: could not allocate buffer"
+					"descriptors\n");
+		ret = -ENOMEM;
+		goto get_property_error;
+	}
+
+	tdm_ctrl[device_num]->tdm_input_data = tdm_buff;
+	tdm_ctrl[device_num]->dma_input_addr = physaddr;
+
+	tdm_ctrl[device_num]->tdm_output_data = tdm_buff + NR_BUFS *
+		SAMPLE_DEPTH * tdm_ctrl[device_num]->cfg_ctrl.active_num_ts;
+	tdm_ctrl[device_num]->dma_output_addr = physaddr + NR_BUFS *
+		SAMPLE_DEPTH * tdm_ctrl[device_num]->cfg_ctrl.active_num_ts;
+
+	init_waitqueue_head(&(tdm_ctrl[device_num]->wakeup_event));
+
+	ret = tdm_init(tdm_ctrl[device_num]);
+	if (ret != 0)
+		goto tdm_init_error;
+
+	ret = tdm_start(tdm_ctrl[device_num]);
+	if (ret != 0)
+		goto tdm_start_error;
+
+	dev_set_drvdata(&(ofdev->dev), tdm_ctrl[device_num]);
+
+	pr_info("%s UCC based tdm module installed\n", __FUNCTION__);
+	return 0;
+
+tdm_start_error:
+	tdm_deinit(tdm_ctrl[device_num]);
+tdm_init_error:
+	dma_free_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
+				tdm_ctrl[device_num]->cfg_ctrl.active_num_ts,
+				tdm_ctrl[device_num]->tdm_input_data,
+				tdm_ctrl[device_num]->dma_input_addr);
+
+get_property_error:
+	kfree(tdm_ctrl[device_num]);
+	return ret;
+}
+
+static int ucc_tdm_remove(struct of_device *ofdev)
+{
+	struct tdm_ctrl *tdm_c;
+	struct ucc_tdm_info *ut_info;
+	u32 ucc_num;
+
+	tdm_c = dev_get_drvdata(&(ofdev->dev));
+	ucc_num = tdm_c->ut_info->uf_info.ucc_num;
+	ut_info = &utdm_info[ucc_num];
+	tdm_stop(tdm_c);
+	tdm_deinit(tdm_c);
+
+	ucc_fast_free(tdm_c->uf_private);
+
+	dma_free_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
+				tdm_c->cfg_ctrl.active_num_ts,
+				tdm_c->tdm_input_data,
+				tdm_c->dma_input_addr);
+
+	num_tdm_devices--;
+	kfree(tdm_c);
+
+	ut_info->ucc_busy = 0;
+
+	pr_info("%s UCC based tdm module uninstalled\n", __FUNCTION__);
+	return 0;
+}
+
+static struct of_device_id ucc_tdm_match[] = {
+	{
+	 .type = "tdm",
+	 .compatible = "fsl,ucc-tdm",
+	 }, {},
+};
+
+MODULE_DEVICE_TABLE(of, ucc_tdm_match);
+
+static struct of_platform_driver ucc_tdm_driver = {
+	.name = DRV_NAME,
+	.match_table = ucc_tdm_match,
+	.probe = ucc_tdm_probe,
+	.remove = ucc_tdm_remove,
+};
+
+static int __init ucc_tdm_init(void)
+{
+	u32 i;
+
+	pr_info("ucc_tdm: " DRV_DESC "\n");
+	for (i = 0; i < 8; i++)
+		memcpy(&(utdm_info[i]), &utdm_primary_info,
+			sizeof(utdm_primary_info));
+
+	return of_register_platform_driver(&ucc_tdm_driver);
+}
+
+static void __exit ucc_tdm_exit(void)
+{
+	of_unregister_platform_driver(&ucc_tdm_driver);
+}
+
+module_init(ucc_tdm_init);
+module_exit(ucc_tdm_exit);
+MODULE_AUTHOR("Freescale Semiconductor, Inc");
+MODULE_DESCRIPTION(DRV_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/ucc_tdm.h b/drivers/misc/ucc_tdm.h
new file mode 100644
index 0000000..d0f376b
--- /dev/null
+++ b/drivers/misc/ucc_tdm.h
@@ -0,0 +1,227 @@
+/*
+ * drivers/misc/ucc_tdm.h
+ *
+ * UCC Based Linux TDM Driver
+ * This driver is designed to support UCC based TDM for PowerPC processors.
+ * This driver can interface with SLIC device to run VOIP kind of
+ * applications.
+ *
+ * Author: Ashish Kalra & Poonam Aggrwal
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef TDM_H
+#define TDM_H
+
+#define NUM_TS 8
+#define ACTIVE_CH 8
+
+/*  SAMPLE_DEPTH is the sample depth is the number of frames before
+ * an interrupt.  Must be a multiple of 4
+ */
+#define SAMPLE_DEPTH 80
+
+/* define the number of Rx interrupts to go by for initial stuttering */
+#define STUTTER_INT_CNT 1
+
+/* BMRx Field Descriptions to specify tstate and rstate in UCC parameter RAM*/
+#define EN_BUS_SNOOPING 0x20
+#define BE_BO		0x10
+
+/* UPSMR Register for Transparent UCC controller Bit definitions*/
+#define NBO	0x00000000	/* Normal Mode 1 bit of data per clock */
+
+/* SI Mode register bit definitions */
+#define NORMAL_OPERATION	0x0000
+#define AUTO_ECHO		0x0400
+#define INTERNAL_LB		0x0800
+#define CONTROL_LB		0x0c00
+#define SIMODE_CRT (0x8000 >> 9)
+#define SIMODE_SL (0x8000 >> 10)
+#define SIMODE_CE (0x8000 >> 11)
+#define SIMODE_FE (0x8000 >> 12)
+#define SIMODE_GM (0x8000 >> 13)
+#define SIMODE_TFSD(val) (val)
+#define SIMODE_RFSD(val) ((val) << 8)
+
+#define SI_TDM_MODE_REGISTER_OFFSET	0
+
+#define R_CM			0x02000000
+#define T_CM			0x02000000
+
+#define SET_RX_SI_RAM(n, val)		\
+		out_be16((u16 *)&qe_immr->sir.rx[(n)*2], (u16)(val))
+
+#define SET_TX_SI_RAM(n, val)		\
+		out_be16((u16 *)&qe_immr->sir.tx[(n)*2], (u16)(val))
+
+/* SI RAM entries */
+#define SIR_LAST 0x0001
+#define SIR_CNT(n) ((n) << 2)
+#define SIR_BYTE 0x0002
+#define SIR_BIT  0x0000
+#define SIR_IDLE  0
+#define SIR_UCC(uccx)	(((uccx+9)) << 5)
+
+/* BRGC Register Bit definitions */
+#define BRGC_RESET	(0x1<<17)
+#define BRGC_EN		(0x1<<16)
+#define BRGC_EXTC_QE	(0x00<<14)
+#define BRGC_EXTC_CLK3	(0x01<<14)
+#define BRGC_EXTC_CLK5	(0x01<<15)
+#define BRGC_EXTC_CLK9	(0x01<<14)
+#define BRGC_EXTC_CLK11	(0x01<<14)
+#define BRGC_EXTC_CLK13	(0x01<<14)
+#define BRGC_EXTC_CLK15	(0x01<<15)
+#define BRGC_ATB	(0x1<<13)
+#define BRGC_DIV16	(0x1)
+
+/* structure representing UCC transparent parameter RAM */
+struct ucc_transparent_pram {
+	__be16 riptr;
+	__be16 tiptr;
+	__be16 res0;
+	__be16 mrblr;
+	__be32 rstate;
+	__be32 rbase;
+	__be16 rbdstat;
+	__be16 rbdlen;
+	__be32 rdptr;
+	__be32 tstate;
+	__be32 tbase;
+	__be16 tbdstat;
+	__be16 tbdlen;
+	__be32 tdptr;
+	__be32 rbptr;
+	__be32 tbptr;
+	__be32 rcrc;
+	__be32 res1;
+	__be32 tcrc;
+	__be32 res2;
+	__be32 res3;
+	__be32 c_mask;
+	__be32 c_pres;
+	__be16 disfc;
+	__be16 crcec;
+	__be32 res4[4];
+	__be16 ts_tmp;
+	__be16 tmp_mb;
+};
+
+#define UCC_TRANSPARENT_PRAM_SIZE	0x100
+
+struct tdm_cfg {
+	u8 com_pin;		/* Common receive and transmit pins
+				 * 0 = separate pins
+				 * 1 = common pins
+				 */
+
+	u8 fr_sync_level;	/* SLx bit Frame Sync Polarity
+				 * 0 = L1R/TSYNC active logic "1"
+				 * 1 = L1R/TSYNC active logic "0"
+				 */
+
+	u8 clk_edge;		/* CEx bit Tx Rx Clock Edge
+				 * 0 = TX data on rising edge of clock
+				 * RX data on falling edge
+				 * 1 = TX data on falling edge of clock
+				 * RX data on rising edge
+				 */
+
+	u8 fr_sync_edge;	/* FEx bit Frame sync edge
+				 * Determine when the sync pulses are sampled
+				 * 0 = Falling edge
+				 * 1 = Rising edge
+				 */
+
+	u8 rx_fr_sync_delay;	/* TFSDx/RFSDx bits Frame Sync Delay
+				 * 00 = no bit delay
+				 * 01 = 1 bit delay
+				 * 10 = 2 bit delay
+				 * 11 = 3 bit delay
+				 */
+
+	u8 tx_fr_sync_delay;	/* TFSDx/RFSDx bits Frame Sync Delay
+				 * 00 = no bit delay
+				 * 01 = 1 bit delay
+				 * 10 = 2 bit delay
+				 * 11 = 3 bit delay
+				 */
+
+	u8 active_num_ts;	/* Number of active time slots in TDM
+				 * assume same active Rx/Tx time slots
+				 */
+};
+
+struct ucc_tdm_info {
+	struct ucc_fast_info uf_info;
+	u32	ucc_busy;
+};
+
+struct tdm_ctrl {
+	struct device *device;
+	struct ucc_fast_private *uf_private;
+	struct ucc_tdm_info *ut_info;
+	u32 tdm_port;		/* port for this tdm:TDMA,TDMB,TDMC,TDMD */
+	u32 si;			/* serial interface: 0 or 1 */
+	struct ucc_fast __iomem *uf_regs;	/* UCC Fast registers */
+	u16 rx_mask[8];		/* Active Receive channels LSB is ch0 */
+	u16 tx_mask[8];		/* Active Transmit channels LSB is ch0 */
+	/* Only channels less than the number of FRAME_SIZE are implemented */
+	struct tdm_cfg cfg_ctrl;	/* Signaling controls configuration */
+	u8 *tdm_input_data;     /* buffer used for Rx by the tdm */
+	u8 *tdm_output_data;    /* buffer used for Tx by the tdm */
+
+	dma_addr_t dma_input_addr; /* dma mapped buffer for TDM Rx */
+	dma_addr_t dma_output_addr; /* dma mapped buffer for TDM Tx */
+	u16 physical_num_ts;	/* physical number of timeslots in the tdm
+				   frame */
+	u32 phase_rx;		/* cycles through 0, 1, 2 */
+	u32 phase_tx;		/* cycles through 0, 1, 2 */
+	/*
+	 * the following two variables are for dealing with "stutter" problem
+	 * "stutter" period is about 20 frames or so, varies depending active
+	 * channel num depending on the sample depth, the code should let a
+	 * few Rx interrupts go by
+	 */
+	u32 tdm_icnt;
+	u32 tdm_flag;
+	struct ucc_transparent_pram __iomem *ucc_pram;
+	struct qe_bd __iomem *tx_bd;
+	struct qe_bd __iomem *rx_bd;
+	u32 ucc_pram_offset;
+	u32 tx_bd_offset;
+	u32 rx_bd_offset;
+	u32 rx_ucode_buf_offset;
+	u32 tx_ucode_buf_offset;
+	bool leg_slic;
+	wait_queue_head_t wakeup_event;
+};
+
+struct tdm_client {
+		u32 driver_handle;
+		void (*tdm_read)(u32 driver_handle, short chn_id,
+					short *pcm_buffer, short len);
+		void (*tdm_write)(u32 driver_handle, short chn_id,
+					short *pcm_buffer, short len);
+		wait_queue_head_t *wakeup_event;
+	};
+
+
+#ifndef CONFIG_TDM_LINEAR_PCM
+	#define MAX_PHASE 2
+	#define NR_BUFS	3
+	#define EFF_ACTIVE_CH ACTIVE_CH
+#else
+	#define MAX_PHASE 1
+	#define NR_BUFS	2
+	#define EFF_ACTIVE_CH ACTIVE_CH / 2
+#endif
+
+#endif
-- 
1.5.2.4

^ permalink raw reply related

* FW:  [PATCH 0/3] UCC TDM driver for MPC83xx platforms
From: Aggrwal Poonam @ 2007-12-10 12:00 UTC (permalink / raw)
  To: linuxppc-dev

=20
There are three patches
[PATCH 1/3] drivers/misc : UCC TDM driver for mpc83xx platforms. This
driver is usable in VoIP iind of applications to interface with SLIC
kind of devices to exchange TDM voice samples.

[PATCH 2/3] arch/ : Platform changes
- device tree entries for UCC TDM driver for MPC8323ERDB platform.
- QE changes related to TDM , like,
	 1) Modified ucc_fast_init so that it can be used by fast UCC
based TDM driver. Mainly changes have been made to configure TDM clocks
and Fsyncs.

	2) Modified get_brg_clk so that it can return the input frequncy
and input source of any BRG by reading the corresponding entries from
device tree.

	3) Added new nodes brg and clocks in the device tree which
represent input clocks for different BRGs.

	4) Modified qe_setbrg accordingly.
- new device tree entries added for "clocks" and "brg"

[PATCH 3/3] Documentation
- Modified Documentation to explain the device tree entries related to
UCC TDM driver and the new nodes added("clocks" and "brg")

The patch applies over a merge of galak's for-2.6.25 plus for-2.6.24
plus of_doc_update branches.
In brief the steps were
git clone
git://git.kernel.org/pub/scm/linux/kernel/git/galak/powerpc.git
powerpc-galak git checkout -b for-2.6.25 origin/for-2.6.25 git checkout
-b for-2.6.24 origin/for-2.6.24 git checkout -b of_doc_update
origin/of_doc_update
git pull . for-2.6.24    # merge the other two
git pull . for-2.6.25=20
git checkout -b tdm      # clean slate for tdm rebase work

Also after applying the patches changes have to be made corresponding to
Tabi's patch "qe: add function qe_clock_source".

The driver has been tested with a VoIP stack and application on
MPC8323ERDB.

With Regards
Poonam=20
=20
=20

^ permalink raw reply

* Re: [PATCH 23/25] powerpc: Rework 4xx clock probing in boot wrapper
From: Josh Boyer @ 2007-12-10 11:54 UTC (permalink / raw)
  To: benh; +Cc: linuxppc-dev
In-Reply-To: <1197265411.6563.56.camel@pasglop>

On Mon, 10 Dec 2007 16:43:31 +1100
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:

> 
> On Thu, 2007-12-06 at 21:27 -0600, Josh Boyer wrote:
> > On Thu, 06 Dec 2007 19:00:22 +1100
> > Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> > 
> > > Index: linux-work/arch/powerpc/boot/reg.h
> > > ===================================================================
> > > --- linux-work.orig/arch/powerpc/boot/reg.h	2007-12-03 14:26:09.000000000 +1100
> > > +++ linux-work/arch/powerpc/boot/reg.h	2007-12-03 14:26:09.000000000 +1100
> > > @@ -24,6 +24,14 @@ static inline u32 mfpvr(void)
> > >  				: "=r" (rval)); rval; })
> > >  #define mtspr(rn, v)	asm volatile("mtspr " __stringify(rn) ",%0" : : "r" (v))
> > > 
> > > +#define __stringify_1(x)	#x
> > > +#define __stringify(x)		__stringify_1(x)
> > > +
> > > +#define mfspr(rn)	({unsigned long rval; \
> > > +			asm volatile("mfspr %0," __stringify(rn) \
> > > +				: "=r" (rval)); rval; })
> > > +#define mtspr(rn, v)	asm volatile("mtspr " __stringify(rn) ",%0" : : "r" (v))
> > > +
> > 
> > You felt like duplicating this?  It was added in the previous patch. :)
> 
> Want a new patch or can you just remove that bit ?

I can remove it on my final apply.

josh

^ permalink raw reply

* (no subject)
From: Aggrwal Poonam @ 2007-12-10 11:58 UTC (permalink / raw)
  To: linuxppc-dev



With Regards
Poonam=20
=20
=20

^ permalink raw reply

* [PATCH] IB/ehca: Return correct #SGEs for SRQ
From: Joachim Fenkes @ 2007-12-10 11:20 UTC (permalink / raw)
  To: LinuxPPC-Dev, LKML, OF-General, Roland Dreier, OF-EWG
  Cc: Stefan Roscher, Christoph Raisch, Marcus Eder

Firmware would round up the number of SGEs to four, because the WQE
structure holds four SGEs. For SRQ, only three are supported, so return a
fixed value instead.

Signed-off-by: Joachim Fenkes <fenkes@de.ibm.com>
---

The patch will apply cleanly on top of Roland's git. Please review and apply
for 2.6.24 -- Thanks!

 drivers/infiniband/hw/ehca/ehca_qp.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c
index dd12668..eff5fb5 100644
--- a/drivers/infiniband/hw/ehca/ehca_qp.c
+++ b/drivers/infiniband/hw/ehca/ehca_qp.c
@@ -838,7 +838,7 @@ struct ib_srq *ehca_create_srq(struct ib_pd *pd,
 
 	/* copy back return values */
 	srq_init_attr->attr.max_wr = qp_init_attr.cap.max_recv_wr;
-	srq_init_attr->attr.max_sge = qp_init_attr.cap.max_recv_sge;
+	srq_init_attr->attr.max_sge = 3;
 
 	/* drive SRQ into RTR state */
 	mqpcb = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
@@ -1750,7 +1750,7 @@ int ehca_query_srq(struct ib_srq *srq, struct ib_srq_attr *srq_attr)
 	}
 
 	srq_attr->max_wr = qpcb->max_nr_outst_recv_wr - 1;
-	srq_attr->max_sge = qpcb->actual_nr_sges_in_rq_wqe;
+	srq_attr->max_sge = 3;
 	srq_attr->srq_limit = EHCA_BMASK_GET(
 		MQPCB_CURR_SRQ_LIMIT, qpcb->curr_srq_limit);
 
-- 
1.5.2

^ permalink raw reply related

* [PATCH] [POWERPC] 4xx: Add 440SPe revA runtime detection to PCIe driver
From: Stefan Roese @ 2007-12-10  7:37 UTC (permalink / raw)
  To: linuxppc-dev

This patch adds runtime detection of the 440SPe revision A chips. These
chips are equipped with a slighly different PCIe core and need special/
different initialization. The compatible node is changed to
"plb-pciex-440spe" ("A" and "B" dropped). This is needed for boards that
can be equipped with both PPC revisions like the AMCC Yucca.

Signed-off-by: Stefan Roese <sr@denx.de>
---
 arch/powerpc/boot/dts/katmai.dts |    6 +++---
 arch/powerpc/sysdev/ppc4xx_pci.c |   25 +++++++++++++++++--------
 2 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/arch/powerpc/boot/dts/katmai.dts b/arch/powerpc/boot/dts/katmai.dts
index 2bea84f..824cf4e 100644
--- a/arch/powerpc/boot/dts/katmai.dts
+++ b/arch/powerpc/boot/dts/katmai.dts
@@ -269,7 +269,7 @@
 			#interrupt-cells = <1>;
 			#size-cells = <2>;
 			#address-cells = <3>;
-			compatible = "ibm,plb-pciex-440speB", "ibm,plb-pciex";
+			compatible = "ibm,plb-pciex-440spe", "ibm,plb-pciex";
 			primary;
 			port = <0>; /* port number */
 			reg = <d 00000000 20000000	/* Config space access */
@@ -310,7 +310,7 @@
 			#interrupt-cells = <1>;
 			#size-cells = <2>;
 			#address-cells = <3>;
-			compatible = "ibm,plb-pciex-440speB", "ibm,plb-pciex";
+			compatible = "ibm,plb-pciex-440spe", "ibm,plb-pciex";
 			primary;
 			port = <1>; /* port number */
 			reg = <d 20000000 20000000	/* Config space access */
@@ -351,7 +351,7 @@
 			#interrupt-cells = <1>;
 			#size-cells = <2>;
 			#address-cells = <3>;
-			compatible = "ibm,plb-pciex-440speB", "ibm,plb-pciex";
+			compatible = "ibm,plb-pciex-440spe", "ibm,plb-pciex";
 			primary;
 			port = <2>; /* port number */
 			reg = <d 40000000 20000000	/* Config space access */
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index bd85a40..744b5ca 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -49,6 +49,15 @@ extern unsigned long total_memory;
 #define RES_TO_U32_HIGH(val)	(0)
 #endif
 
+static inline int ppc440spe_revA(void)
+{
+	/* Catch both 440SPe variants, with and without RAID6 support */
+        if ((mfspr(SPRN_PVR) & 0xffefffff) == 0x53421890)
+                return 1;
+        else
+                return 0;
+}
+
 static void fixup_ppc4xx_pci_bridge(struct pci_dev *dev)
 {
 	struct pci_controller *hose;
@@ -521,8 +530,7 @@ static void __init ppc4xx_probe_pcix_bridge(struct device_node *np)
  *
  * We support 3 parts currently based on the compatible property:
  *
- * ibm,plb-pciex-440speA
- * ibm,plb-pciex-440speB
+ * ibm,plb-pciex-440spe
  * ibm,plb-pciex-405ex
  *
  * Anything else will be rejected for now as they are all subtly
@@ -693,7 +701,7 @@ static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
 
 	mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val);
 	mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x20222222);
-	if (of_device_is_compatible(port->node, "ibm,plb-pciex-440speA"))
+	if (ppc440spe_revA())
 		mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x11000000);
 	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL0SET1, 0x35000000);
 	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL1SET1, 0x35000000);
@@ -772,7 +780,6 @@ static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata =
 	.setup_utl	= ppc440speB_pciex_init_utl,
 };
 
-
 #endif /* CONFIG_44x */
 
 #ifdef CONFIG_40x
@@ -888,10 +895,12 @@ static int __init ppc4xx_pciex_check_core_init(struct device_node *np)
 		return 0;
 
 #ifdef CONFIG_44x
-	if (of_device_is_compatible(np, "ibm,plb-pciex-440speA"))
-		ppc4xx_pciex_hwops = &ppc440speA_pcie_hwops;
-	else if (of_device_is_compatible(np, "ibm,plb-pciex-440speB"))
-		ppc4xx_pciex_hwops = &ppc440speB_pcie_hwops;
+	if (of_device_is_compatible(np, "ibm,plb-pciex-440spe")) {
+		if (ppc440spe_revA())
+			ppc4xx_pciex_hwops = &ppc440speA_pcie_hwops;
+		else
+			ppc4xx_pciex_hwops = &ppc440speB_pcie_hwops;
+	}
 #endif /* CONFIG_44x    */
 #ifdef CONFIG_40x
 	if (of_device_is_compatible(np, "ibm,plb-pciex-405ex"))
-- 
1.5.3.7.949.g2221a6

^ permalink raw reply related

* [PATCH 2/2] pci: Fix warning in setup-res.c on 32-bit platforms with 64-bit resources
From: Benjamin Herrenschmidt @ 2007-12-10  6:32 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linuxppc-dev, linux-pci, linux-kernel

This adds appropriate casts to avoid a warning and print the correct
values in pr_debug.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

 drivers/pci/setup-res.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

Index: linux-work/drivers/pci/setup-res.c
===================================================================
--- linux-work.orig/drivers/pci/setup-res.c	2007-12-10 17:17:32.000000000 +1100
+++ linux-work/drivers/pci/setup-res.c	2007-12-10 17:17:54.000000000 +1100
@@ -51,10 +51,12 @@ pci_update_resource(struct pci_dev *dev,
 
 	pcibios_resource_to_bus(dev, &region, res);
 
-	pr_debug("  got res [%llx:%llx] bus [%lx:%lx] flags %lx for "
+	pr_debug("  got res [%llx:%llx] bus [%llx:%llx] flags %lx for "
 		 "BAR %d of %s\n", (unsigned long long)res->start,
 		 (unsigned long long)res->end,
-		 region.start, region.end, res->flags, resno, pci_name(dev));
+		 (unsigned long long)region.start,
+		 (unsigned long long)region.end,
+		 (unsigned long)res->flags, resno, pci_name(dev));
 
 	new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
 	if (res->flags & IORESOURCE_IO)

^ permalink raw reply

* [PATCH 1/2] pci: Fix bus resource assignment on 32 bits with 64b resources
From: Benjamin Herrenschmidt @ 2007-12-10  6:32 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linuxppc-dev, linux-pci, linux-kernel

The current pci_assign_unassigned_resources() code doesn't work properly
on 32 bits platforms with 64 bits resources. The main reason is the use
of unsigned long in various places instead of resource_size_t.

This fixes it, along with some tricks to avoid casting to 64 bits on
platforms that don't need it in every printk around.

This is a pre-requisite for making powerpc use the generic code instead of
its own half-useful implementation.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

This version now uses casts as Greg asked for and adds proper setup
of the prefetchable base & limit "upper" registers when using 64 bits
resources.

(and builds ... sorry about that)

 drivers/pci/setup-bus.c |   64 ++++++++++++++++++++++++++++++------------------
 include/linux/pci.h     |    4 +--
 2 files changed, 42 insertions(+), 26 deletions(-)

Index: linux-work/drivers/pci/setup-bus.c
===================================================================
--- linux-work.orig/drivers/pci/setup-bus.c	2007-12-10 17:16:44.000000000 +1100
+++ linux-work/drivers/pci/setup-bus.c	2007-12-10 17:21:53.000000000 +1100
@@ -89,8 +89,9 @@ void pci_setup_cardbus(struct pci_bus *b
 		 * The IO resource is allocated a range twice as large as it
 		 * would normally need.  This allows us to set both IO regs.
 		 */
-		printk("  IO window: %08lx-%08lx\n",
-			region.start, region.end);
+		printk(KERN_INFO "  IO window: 0x%08lx-0x%08lx\n",
+		       (unsigned long)region.start,
+		       (unsigned long)region.end);
 		pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
 					region.start);
 		pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0,
@@ -99,8 +100,9 @@ void pci_setup_cardbus(struct pci_bus *b
 
 	pcibios_resource_to_bus(bridge, &region, bus->resource[1]);
 	if (bus->resource[1]->flags & IORESOURCE_IO) {
-		printk("  IO window: %08lx-%08lx\n",
-			region.start, region.end);
+		printk(KERN_INFO "  IO window: 0x%08lx-0x%08lx\n",
+		       (unsigned long)region.start,
+		       (unsigned long)region.end);
 		pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
 					region.start);
 		pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1,
@@ -109,8 +111,9 @@ void pci_setup_cardbus(struct pci_bus *b
 
 	pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
 	if (bus->resource[2]->flags & IORESOURCE_MEM) {
-		printk("  PREFETCH window: %08lx-%08lx\n",
-			region.start, region.end);
+		printk(KERN_INFO "  PREFETCH window: 0x%08lx-0x%08lx\n",
+		       (unsigned long)region.start,
+		       (unsigned long)region.end);
 		pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
 					region.start);
 		pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0,
@@ -119,8 +122,9 @@ void pci_setup_cardbus(struct pci_bus *b
 
 	pcibios_resource_to_bus(bridge, &region, bus->resource[3]);
 	if (bus->resource[3]->flags & IORESOURCE_MEM) {
-		printk("  MEM window: %08lx-%08lx\n",
-			region.start, region.end);
+		printk(KERN_INFO "  MEM window: 0x%08lx-0x%08lx\n",
+		       (unsigned long)region.start,
+		       (unsigned long)region.end);
 		pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
 					region.start);
 		pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1,
@@ -145,7 +149,7 @@ pci_setup_bridge(struct pci_bus *bus)
 {
 	struct pci_dev *bridge = bus->self;
 	struct pci_bus_region region;
-	u32 l, io_upper16;
+	u32 l, bu, lu, io_upper16;
 
 	DBG(KERN_INFO "PCI: Bridge: %s\n", pci_name(bridge));
 
@@ -159,7 +163,8 @@ pci_setup_bridge(struct pci_bus *bus)
 		/* Set up upper 16 bits of I/O base/limit. */
 		io_upper16 = (region.end & 0xffff0000) | (region.start >> 16);
 		DBG(KERN_INFO "  IO window: %04lx-%04lx\n",
-				region.start, region.end);
+		    (unsigned long)region.start,
+		    (unsigned long)region.end);
 	}
 	else {
 		/* Clear upper 16 bits of I/O base/limit. */
@@ -180,8 +185,9 @@ pci_setup_bridge(struct pci_bus *bus)
 	if (bus->resource[1]->flags & IORESOURCE_MEM) {
 		l = (region.start >> 16) & 0xfff0;
 		l |= region.end & 0xfff00000;
-		DBG(KERN_INFO "  MEM window: %08lx-%08lx\n",
-				region.start, region.end);
+		DBG(KERN_INFO "  MEM window: 0x%08lx-0x%08lx\n",
+		    (unsigned long)region.start,
+		    (unsigned long)region.end);
 	}
 	else {
 		l = 0x0000fff0;
@@ -195,12 +201,18 @@ pci_setup_bridge(struct pci_bus *bus)
 	pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);
 
 	/* Set up PREF base/limit. */
+	bu = lu = 0;
 	pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
 	if (bus->resource[2]->flags & IORESOURCE_PREFETCH) {
 		l = (region.start >> 16) & 0xfff0;
 		l |= region.end & 0xfff00000;
-		DBG(KERN_INFO "  PREFETCH window: %08lx-%08lx\n",
-				region.start, region.end);
+#ifdef CONFIG_RESOURCES_64BIT
+		bu = region.start >> 32;
+		lu = region.end >> 32;
+#endif
+		DBG(KERN_INFO "  PREFETCH window: 0x%016llx-0x%016llx\n",
+		    (unsigned long long)region.start,
+		    (unsigned long long)region.end);
 	}
 	else {
 		l = 0x0000fff0;
@@ -208,8 +220,9 @@ pci_setup_bridge(struct pci_bus *bus)
 	}
 	pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
 
-	/* Clear out the upper 32 bits of PREF base. */
-	pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, 0);
+	/* Set the upper 32 bits of PREF base & limit. */
+	pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
+	pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
 
 	pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
 }
@@ -323,8 +336,8 @@ static void pbus_size_io(struct pci_bus 
 static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type)
 {
 	struct pci_dev *dev;
-	unsigned long min_align, align, size;
-	unsigned long aligns[12];	/* Alignments from 1Mb to 2Gb */
+	resource_size_t min_align, align, size;
+	resource_size_t aligns[12];	/* Alignments from 1Mb to 2Gb */
 	int order, max_order;
 	struct resource *b_res = find_free_bus_resource(bus, type);
 
@@ -340,7 +353,7 @@ static int pbus_size_mem(struct pci_bus 
 		
 		for (i = 0; i < PCI_NUM_RESOURCES; i++) {
 			struct resource *r = &dev->resource[i];
-			unsigned long r_size;
+			resource_size_t r_size;
 
 			if (r->parent || (r->flags & mask) != type)
 				continue;
@@ -350,10 +363,10 @@ static int pbus_size_mem(struct pci_bus 
 			order = __ffs(align) - 20;
 			if (order > 11) {
 				printk(KERN_WARNING "PCI: region %s/%d "
-				       "too large: %llx-%llx\n",
+				       "too large: 0x%016llx-0x%016llx\n",
 					pci_name(dev), i,
-					(unsigned long long)r->start,
-					(unsigned long long)r->end);
+				       (unsigned long long)r->start,
+				       (unsigned long long)r->end);
 				r->flags = 0;
 				continue;
 			}
@@ -372,8 +385,11 @@ static int pbus_size_mem(struct pci_bus 
 	align = 0;
 	min_align = 0;
 	for (order = 0; order <= max_order; order++) {
-		unsigned long align1 = 1UL << (order + 20);
-
+#ifdef CONFIG_RESOURCES_64BIT
+		resource_size_t align1 = 1ULL << (order + 20);
+#else
+		resource_size_t align1 = 1U << (order + 20);
+#endif
 		if (!align)
 			min_align = align1;
 		else if (ALIGN(align + min_align, min_align) < align1)
Index: linux-work/include/linux/pci.h
===================================================================
--- linux-work.orig/include/linux/pci.h	2007-12-10 17:16:44.000000000 +1100
+++ linux-work/include/linux/pci.h	2007-12-10 17:16:46.000000000 +1100
@@ -314,8 +314,8 @@ struct pci_raw_ops {
 extern struct pci_raw_ops *raw_pci_ops;
 
 struct pci_bus_region {
-	unsigned long start;
-	unsigned long end;
+	resource_size_t start;
+	resource_size_t end;
 };
 
 struct pci_dynids {

^ 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