LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V5 3/3] powerpc/mm: Switch to generic RCU get_user_pages_fast
From: Aneesh Kumar K.V @ 2014-11-05 16:27 UTC (permalink / raw)
  To: akpm, Steve Capper, Andrea Arcangeli, benh, mpe, David Miller
  Cc: linux-arch, linux-mm, linuxppc-dev, linux-kernel,
	Aneesh Kumar K.V
In-Reply-To: <1415204861-22016-1-git-send-email-aneesh.kumar@linux.vnet.ibm.com>

This patch switch the ppc arch to use the generic RCU based
gup implementation.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
---
 arch/powerpc/Kconfig                     |   1 +
 arch/powerpc/include/asm/hugetlb.h       |   8 +-
 arch/powerpc/include/asm/page.h          |   3 +-
 arch/powerpc/include/asm/pgtable-ppc64.h |   1 -
 arch/powerpc/include/asm/pgtable.h       |   6 +-
 arch/powerpc/mm/Makefile                 |   2 +-
 arch/powerpc/mm/gup.c                    | 235 -------------------------------
 arch/powerpc/mm/hugetlbpage.c            |  33 ++---
 8 files changed, 22 insertions(+), 267 deletions(-)
 delete mode 100644 arch/powerpc/mm/gup.c

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 88eace4e28c3..7af887dc6aed 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -148,6 +148,7 @@ config PPC
 	select HAVE_ARCH_AUDITSYSCALL
 	select ARCH_SUPPORTS_ATOMIC_RMW
 	select DCACHE_WORD_ACCESS if PPC64 && CPU_LITTLE_ENDIAN
+	select HAVE_GENERIC_RCU_GUP
 
 config GENERIC_CSUM
 	def_bool CPU_LITTLE_ENDIAN
diff --git a/arch/powerpc/include/asm/hugetlb.h b/arch/powerpc/include/asm/hugetlb.h
index 766b77d527ac..1d53a65b4ec1 100644
--- a/arch/powerpc/include/asm/hugetlb.h
+++ b/arch/powerpc/include/asm/hugetlb.h
@@ -48,7 +48,7 @@ static inline unsigned int hugepd_shift(hugepd_t hpd)
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
 
-static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr,
+static inline pte_t *hugepte_offset(hugepd_t hpd, unsigned long addr,
 				    unsigned pdshift)
 {
 	/*
@@ -58,9 +58,9 @@ static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr,
 	 */
 	unsigned long idx = 0;
 
-	pte_t *dir = hugepd_page(*hpdp);
+	pte_t *dir = hugepd_page(hpd);
 #ifndef CONFIG_PPC_FSL_BOOK3E
-	idx = (addr & ((1UL << pdshift) - 1)) >> hugepd_shift(*hpdp);
+	idx = (addr & ((1UL << pdshift) - 1)) >> hugepd_shift(hpd);
 #endif
 
 	return dir + idx;
@@ -193,7 +193,7 @@ static inline void flush_hugetlb_page(struct vm_area_struct *vma,
 }
 
 #define hugepd_shift(x) 0
-static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr,
+static inline pte_t *hugepte_offset(hugepd_t hpd, unsigned long addr,
 				    unsigned pdshift)
 {
 	return 0;
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index f973fce73a43..69c059887a2c 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -379,13 +379,14 @@ static inline int hugepd_ok(hugepd_t hpd)
 }
 #endif
 
-#define is_hugepd(pdep)               (hugepd_ok(*((hugepd_t *)(pdep))))
+#define is_hugepd(hpd)               (hugepd_ok(hpd))
 #define pgd_huge pgd_huge
 int pgd_huge(pgd_t pgd);
 #else /* CONFIG_HUGETLB_PAGE */
 #define is_hugepd(pdep)			0
 #define pgd_huge(pgd)			0
 #endif /* CONFIG_HUGETLB_PAGE */
+#define __hugepd(x) ((hugepd_t) { (x) })
 
 struct page;
 extern void clear_user_page(void *page, unsigned long vaddr, struct page *pg);
diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/include/asm/pgtable-ppc64.h
index d12092420560..5600e434332f 100644
--- a/arch/powerpc/include/asm/pgtable-ppc64.h
+++ b/arch/powerpc/include/asm/pgtable-ppc64.h
@@ -600,6 +600,5 @@ static inline int pmd_move_must_withdraw(struct spinlock *new_pmd_ptl,
 	 */
 	return true;
 }
-
 #endif /* __ASSEMBLY__ */
 #endif /* _ASM_POWERPC_PGTABLE_PPC64_H_ */
diff --git a/arch/powerpc/include/asm/pgtable.h b/arch/powerpc/include/asm/pgtable.h
index 316f9a5da173..a8805fee0df9 100644
--- a/arch/powerpc/include/asm/pgtable.h
+++ b/arch/powerpc/include/asm/pgtable.h
@@ -274,11 +274,9 @@ extern void paging_init(void);
  */
 extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t *);
 
-extern int gup_hugepd(hugepd_t *hugepd, unsigned pdshift, unsigned long addr,
-		      unsigned long end, int write, struct page **pages, int *nr);
-
 extern int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long addr,
-		       unsigned long end, int write, struct page **pages, int *nr);
+		       unsigned long end, int write,
+		       struct page **pages, int *nr);
 #ifndef CONFIG_TRANSPARENT_HUGEPAGE
 #define pmd_large(pmd)		0
 #define has_transparent_hugepage() 0
diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile
index 325e861616a1..438dcd3fd0d1 100644
--- a/arch/powerpc/mm/Makefile
+++ b/arch/powerpc/mm/Makefile
@@ -6,7 +6,7 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
 ccflags-$(CONFIG_PPC64)	:= $(NO_MINIMAL_TOC)
 
-obj-y				:= fault.o mem.o pgtable.o gup.o mmap.o \
+obj-y				:= fault.o mem.o pgtable.o mmap.o \
 				   init_$(CONFIG_WORD_SIZE).o \
 				   pgtable_$(CONFIG_WORD_SIZE).o
 obj-$(CONFIG_PPC_MMU_NOHASH)	+= mmu_context_nohash.o tlb_nohash.o \
diff --git a/arch/powerpc/mm/gup.c b/arch/powerpc/mm/gup.c
deleted file mode 100644
index d8746684f606..000000000000
--- a/arch/powerpc/mm/gup.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Lockless get_user_pages_fast for powerpc
- *
- * Copyright (C) 2008 Nick Piggin
- * Copyright (C) 2008 Novell Inc.
- */
-#undef DEBUG
-
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/hugetlb.h>
-#include <linux/vmstat.h>
-#include <linux/pagemap.h>
-#include <linux/rwsem.h>
-#include <asm/pgtable.h>
-
-#ifdef __HAVE_ARCH_PTE_SPECIAL
-
-/*
- * The performance critical leaf functions are made noinline otherwise gcc
- * inlines everything into a single function which results in too much
- * register pressure.
- */
-static noinline int gup_pte_range(pmd_t pmd, unsigned long addr,
-		unsigned long end, int write, struct page **pages, int *nr)
-{
-	unsigned long mask, result;
-	pte_t *ptep;
-
-	result = _PAGE_PRESENT|_PAGE_USER;
-	if (write)
-		result |= _PAGE_RW;
-	mask = result | _PAGE_SPECIAL;
-
-	ptep = pte_offset_kernel(&pmd, addr);
-	do {
-		pte_t pte = ACCESS_ONCE(*ptep);
-		struct page *page;
-		/*
-		 * Similar to the PMD case, NUMA hinting must take slow path
-		 */
-		if (pte_numa(pte))
-			return 0;
-
-		if ((pte_val(pte) & mask) != result)
-			return 0;
-		VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
-		page = pte_page(pte);
-		if (!page_cache_get_speculative(page))
-			return 0;
-		if (unlikely(pte_val(pte) != pte_val(*ptep))) {
-			put_page(page);
-			return 0;
-		}
-		pages[*nr] = page;
-		(*nr)++;
-
-	} while (ptep++, addr += PAGE_SIZE, addr != end);
-
-	return 1;
-}
-
-static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
-		int write, struct page **pages, int *nr)
-{
-	unsigned long next;
-	pmd_t *pmdp;
-
-	pmdp = pmd_offset(&pud, addr);
-	do {
-		pmd_t pmd = ACCESS_ONCE(*pmdp);
-
-		next = pmd_addr_end(addr, end);
-		/*
-		 * If we find a splitting transparent hugepage we
-		 * return zero. That will result in taking the slow
-		 * path which will call wait_split_huge_page()
-		 * if the pmd is still in splitting state
-		 */
-		if (pmd_none(pmd) || pmd_trans_splitting(pmd))
-			return 0;
-		if (pmd_huge(pmd) || pmd_large(pmd)) {
-			/*
-			 * NUMA hinting faults need to be handled in the GUP
-			 * slowpath for accounting purposes and so that they
-			 * can be serialised against THP migration.
-			 */
-			if (pmd_numa(pmd))
-				return 0;
-
-			if (!gup_hugepte((pte_t *)pmdp, PMD_SIZE, addr, next,
-					 write, pages, nr))
-				return 0;
-		} else if (is_hugepd(pmdp)) {
-			if (!gup_hugepd((hugepd_t *)pmdp, PMD_SHIFT,
-					addr, next, write, pages, nr))
-				return 0;
-		} else if (!gup_pte_range(pmd, addr, next, write, pages, nr))
-			return 0;
-	} while (pmdp++, addr = next, addr != end);
-
-	return 1;
-}
-
-static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end,
-		int write, struct page **pages, int *nr)
-{
-	unsigned long next;
-	pud_t *pudp;
-
-	pudp = pud_offset(&pgd, addr);
-	do {
-		pud_t pud = ACCESS_ONCE(*pudp);
-
-		next = pud_addr_end(addr, end);
-		if (pud_none(pud))
-			return 0;
-		if (pud_huge(pud)) {
-			if (!gup_hugepte((pte_t *)pudp, PUD_SIZE, addr, next,
-					 write, pages, nr))
-				return 0;
-		} else if (is_hugepd(pudp)) {
-			if (!gup_hugepd((hugepd_t *)pudp, PUD_SHIFT,
-					addr, next, write, pages, nr))
-				return 0;
-		} else if (!gup_pmd_range(pud, addr, next, write, pages, nr))
-			return 0;
-	} while (pudp++, addr = next, addr != end);
-
-	return 1;
-}
-
-int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
-			  struct page **pages)
-{
-	struct mm_struct *mm = current->mm;
-	unsigned long addr, len, end;
-	unsigned long next;
-	unsigned long flags;
-	pgd_t *pgdp;
-	int nr = 0;
-
-	pr_devel("%s(%lx,%x,%s)\n", __func__, start, nr_pages, write ? "write" : "read");
-
-	start &= PAGE_MASK;
-	addr = start;
-	len = (unsigned long) nr_pages << PAGE_SHIFT;
-	end = start + len;
-
-	if (unlikely(!access_ok(write ? VERIFY_WRITE : VERIFY_READ,
-					start, len)))
-		return 0;
-
-	pr_devel("  aligned: %lx .. %lx\n", start, end);
-
-	/*
-	 * XXX: batch / limit 'nr', to avoid large irq off latency
-	 * needs some instrumenting to determine the common sizes used by
-	 * important workloads (eg. DB2), and whether limiting the batch size
-	 * will decrease performance.
-	 *
-	 * It seems like we're in the clear for the moment. Direct-IO is
-	 * the main guy that batches up lots of get_user_pages, and even
-	 * they are limited to 64-at-a-time which is not so many.
-	 */
-	/*
-	 * This doesn't prevent pagetable teardown, but does prevent
-	 * the pagetables from being freed on powerpc.
-	 *
-	 * So long as we atomically load page table pointers versus teardown,
-	 * we can follow the address down to the the page and take a ref on it.
-	 */
-	local_irq_save(flags);
-
-	pgdp = pgd_offset(mm, addr);
-	do {
-		pgd_t pgd = ACCESS_ONCE(*pgdp);
-
-		pr_devel("  %016lx: normal pgd %p\n", addr,
-			 (void *)pgd_val(pgd));
-		next = pgd_addr_end(addr, end);
-		if (pgd_none(pgd))
-			break;
-		if (pgd_huge(pgd)) {
-			if (!gup_hugepte((pte_t *)pgdp, PGDIR_SIZE, addr, next,
-					 write, pages, &nr))
-				break;
-		} else if (is_hugepd(pgdp)) {
-			if (!gup_hugepd((hugepd_t *)pgdp, PGDIR_SHIFT,
-					addr, next, write, pages, &nr))
-				break;
-		} else if (!gup_pud_range(pgd, addr, next, write, pages, &nr))
-			break;
-	} while (pgdp++, addr = next, addr != end);
-
-	local_irq_restore(flags);
-
-	return nr;
-}
-
-int get_user_pages_fast(unsigned long start, int nr_pages, int write,
-			struct page **pages)
-{
-	struct mm_struct *mm = current->mm;
-	int nr, ret;
-
-	start &= PAGE_MASK;
-	nr = __get_user_pages_fast(start, nr_pages, write, pages);
-	ret = nr;
-
-	if (nr < nr_pages) {
-		pr_devel("  slow path ! nr = %d\n", nr);
-
-		/* Try to get the remaining pages with get_user_pages */
-		start += nr << PAGE_SHIFT;
-		pages += nr;
-
-		down_read(&mm->mmap_sem);
-		ret = get_user_pages(current, mm, start,
-				     nr_pages - nr, write, 0, pages, NULL);
-		up_read(&mm->mmap_sem);
-
-		/* Have to be a bit careful with return values */
-		if (nr > 0) {
-			if (ret < 0)
-				ret = nr;
-			else
-				ret += nr;
-		}
-	}
-
-	return ret;
-}
-
-#endif /* __HAVE_ARCH_PTE_SPECIAL */
diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
index 2e35b9a82929..d54d74ccdc5a 100644
--- a/arch/powerpc/mm/hugetlbpage.c
+++ b/arch/powerpc/mm/hugetlbpage.c
@@ -233,7 +233,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr, unsigned long sz
 	if (hugepd_none(*hpdp) && __hugepte_alloc(mm, hpdp, addr, pdshift, pshift))
 		return NULL;
 
-	return hugepte_offset(hpdp, addr, pdshift);
+	return hugepte_offset(*hpdp, addr, pdshift);
 }
 
 #else
@@ -273,7 +273,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr, unsigned long sz
 	if (hugepd_none(*hpdp) && __hugepte_alloc(mm, hpdp, addr, pdshift, pshift))
 		return NULL;
 
-	return hugepte_offset(hpdp, addr, pdshift);
+	return hugepte_offset(*hpdp, addr, pdshift);
 }
 #endif
 
@@ -541,7 +541,7 @@ static void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud,
 	do {
 		pmd = pmd_offset(pud, addr);
 		next = pmd_addr_end(addr, end);
-		if (!is_hugepd(pmd)) {
+		if (!is_hugepd(__hugepd(pmd_val(*pmd)))) {
 			/*
 			 * if it is not hugepd pointer, we should already find
 			 * it cleared.
@@ -590,7 +590,7 @@ static void hugetlb_free_pud_range(struct mmu_gather *tlb, pgd_t *pgd,
 	do {
 		pud = pud_offset(pgd, addr);
 		next = pud_addr_end(addr, end);
-		if (!is_hugepd(pud)) {
+		if (!is_hugepd(__hugepd(pud_val(*pud)))) {
 			if (pud_none_or_clear_bad(pud))
 				continue;
 			hugetlb_free_pmd_range(tlb, pud, addr, next, floor,
@@ -656,7 +656,7 @@ void hugetlb_free_pgd_range(struct mmu_gather *tlb,
 	do {
 		next = pgd_addr_end(addr, end);
 		pgd = pgd_offset(tlb->mm, addr);
-		if (!is_hugepd(pgd)) {
+		if (!is_hugepd(__hugepd(pgd_val(*pgd)))) {
 			if (pgd_none_or_clear_bad(pgd))
 				continue;
 			hugetlb_free_pud_range(tlb, pgd, addr, next, floor, ceiling);
@@ -716,12 +716,11 @@ static unsigned long hugepte_addr_end(unsigned long addr, unsigned long end,
 	return (__boundary - 1 < end - 1) ? __boundary : end;
 }
 
-int gup_hugepd(hugepd_t *hugepd, unsigned pdshift,
-	       unsigned long addr, unsigned long end,
-	       int write, struct page **pages, int *nr)
+int gup_huge_pd(hugepd_t hugepd, unsigned long addr, unsigned pdshift,
+		unsigned long end, int write, struct page **pages, int *nr)
 {
 	pte_t *ptep;
-	unsigned long sz = 1UL << hugepd_shift(*hugepd);
+	unsigned long sz = 1UL << hugepd_shift(hugepd);
 	unsigned long next;
 
 	ptep = hugepte_offset(hugepd, addr, pdshift);
@@ -964,7 +963,7 @@ pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift
 	else if (pgd_huge(pgd)) {
 		ret_pte = (pte_t *) pgdp;
 		goto out;
-	} else if (is_hugepd(&pgd))
+	} else if (is_hugepd(__hugepd(pgd_val(pgd))))
 		hpdp = (hugepd_t *)&pgd;
 	else {
 		/*
@@ -981,7 +980,7 @@ pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift
 		else if (pud_huge(pud)) {
 			ret_pte = (pte_t *) pudp;
 			goto out;
-		} else if (is_hugepd(&pud))
+		} else if (is_hugepd(__hugepd(pud_val(pud))))
 			hpdp = (hugepd_t *)&pud;
 		else {
 			pdshift = PMD_SHIFT;
@@ -1002,7 +1001,7 @@ pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift
 			if (pmd_huge(pmd) || pmd_large(pmd)) {
 				ret_pte = (pte_t *) pmdp;
 				goto out;
-			} else if (is_hugepd(&pmd))
+			} else if (is_hugepd(__hugepd(pmd_val(pmd))))
 				hpdp = (hugepd_t *)&pmd;
 			else
 				return pte_offset_kernel(&pmd, ea);
@@ -1011,7 +1010,7 @@ pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift
 	if (!hpdp)
 		return NULL;
 
-	ret_pte = hugepte_offset(hpdp, ea, pdshift);
+	ret_pte = hugepte_offset(*hpdp, ea, pdshift);
 	pdshift = hugepd_shift(*hpdp);
 out:
 	if (shift)
@@ -1041,14 +1040,6 @@ int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long addr,
 	if ((pte_val(pte) & mask) != mask)
 		return 0;
 
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-	/*
-	 * check for splitting here
-	 */
-	if (pmd_trans_splitting(pte_pmd(pte)))
-		return 0;
-#endif
-
 	/* hugepages are never "special" */
 	VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
 
-- 
2.1.0

^ permalink raw reply related

* Re: [PATCH 1/2] irqdomain: add support for creating a continous mapping
From: Johannes Thumshirn @ 2014-11-05 16:55 UTC (permalink / raw)
  To: Scott Wood; +Cc: Johannes Thumshirn, Sebastian Andrzej Siewior, linuxppc-dev
In-Reply-To: <1415053131.23458.287.camel@snotra.buserror.net>

On Mon, Nov 03, 2014 at 04:18:51PM -0600, Scott Wood wrote:
> On Mon, 2014-11-03 at 17:18 +0100, Johannes Thumshirn wrote:
> > A MSI device may have multiple interrupts. That means that the
> > interrupts numbers should be continuos so that pdev->irq refers to the
> > first interrupt, pdev->irq + 1 to the second and so on.
> > This patch adds support for continuous allocation of virqs for a range
> > of hwirqs. The function is based on irq_create_mapping() but due to the
> > number argument there is very little in common now.
> >
> > Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> > Signed-off-by: Johannes Thumshirn <johannes.thumshirn@men.de>
> > ---
> >  include/linux/irqdomain.h | 10 ++++--
> >  kernel/irq/irqdomain.c    | 85 ++++++++++++++++++++++++++++++++++++-----------
> >  2 files changed, 73 insertions(+), 22 deletions(-)
>
> This is changing core kernel code and thus LKML should be CCed, as well
> as Ben Herrenschmidt who is the maintainer of kernel/irq/irqdomain.c.
>
> Also please respond to feedback in
> http://patchwork.ozlabs.org/patch/322497/
>
> Is it really necessary for the virqs to be contiguous?  How is the
> availability of multiple MSIs communicated to the driver?  Is there an
> example of a driver that currently uses multiple MSIs?
>
> >  /**
> > - * irq_create_mapping() - Map a hardware interrupt into linux irq space
> > + * irq_create_mapping_block() - Map multiple hardware interrupts
> >   * @domain: domain owning this hardware interrupt or NULL for default domain
> >   * @hwirq: hardware irq number in that domain space
> > + * @num: number of interrupts
> > + *
> > + * Maps a hwirq to a newly allocated virq. If num is greater than 1 then num
> > + * hwirqs (hwirq ... hwirq + num - 1) will be mapped and virq will be
> > + * continuous.
> > + * Returns the first linux virq number.
> >   *
> > - * Only one mapping per hardware interrupt is permitted. Returns a linux
> > - * irq number.
> >   * If the sense/trigger is to be specified, set_irq_type() should be called
> >   * on the number returned from that call.
> >   */
> > -unsigned int irq_create_mapping(struct irq_domain *domain,
> > +unsigned int irq_create_mapping_block(struct irq_domain *domain,
> >  				irq_hw_number_t hwirq)
> >  {
>
> Where is the num parameter?  How does this even build?

F**k this was an error from porting the patch from 3.4.x to 3.18.

>
> >  	unsigned int hint;
> >  	int virq;
> > +	int node;
> > +	int i;
> >
> > -	pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
> > +	pr_debug("%s(0x%p, 0x%lx, %d)\n", __func__, domain, hwirq, num);
> >
> >  	/* Look for default domain if nececssary */
> > -	if (domain == NULL)
> > +	if (!domain && num == 1)
> >  		domain = irq_default_domain;
> > +
> >  	if (domain == NULL) {
> >  		WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
> >  		return 0;
> > @@ -403,35 +437,46 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
> >  	pr_debug("-> using domain @%p\n", domain);
> >
> >  	/* Check if mapping already exists */
> > -	virq = irq_find_mapping(domain, hwirq);
> > -	if (virq) {
> > -		pr_debug("-> existing mapping on virq %d\n", virq);
> > -		return virq;
> > +	for (i = 0; i < num; i++) {
> > +		virq = irq_find_mapping(domain, hwirq + i);
> > +		if (virq != NO_IRQ) {
>
> Please don't introduce new uses of NO_IRQ.  irq_find_mapping() returns
> zero on failure.  Some architectures (e.g. ARM) define NO_IRQ as
> something other than zero, which will cause this to break.

OK

>
> > +			if (i == 0)
> > +				return irq_check_continuous_mapping(domain,
> > +								hwirq, num);
> > +			pr_err("irq: hwirq %ld has no mapping but hwirq %ld "
> > +				"maps to virq %d. This can't be a block\n",
> > +				hwirq, hwirq + i, virq);
> > +			return -EINVAL;
> > +		}
> >  	}
>
> Explain how you'd get into this error state, and how you'd avoid doing
> so.

According to Thomas Gleixner (http://patchwork.ozlabs.org/patch/322497/) the
whole loop is nonsense, so I'll have to rework it anyways.

>
> > +	node = of_node_to_nid(domain->of_node);
> > +
> >  	/* Allocate a virtual interrupt number */
> >  	hint = hwirq % nr_irqs;
> >  	if (hint == 0)
> >  		hint++;
> > -	virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
> > -	if (virq <= 0)
> > -		virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
> > +	virq = irq_alloc_desc_from(hint, node);
> > +	if (virq <= 0 && hint != 1)
> > +		virq = irq_alloc_desc_from(1, node);
>
> Factoring out node seems irrelevant to, and obscures, what you're doing
> which is adding a chcek for hint.  Why is a hint value of 1 special?
>

OK

> You're also still allocating only one virq, unlike in
> http://patchwork.ozlabs.org/patch/322497/
>
> -Scott
>
>

^ permalink raw reply

* Re: powerpc: mitigate impact of decrementer reset
From: Paul Clarke @ 2014-11-05 17:06 UTC (permalink / raw)
  To: Michael Ellerman, linuxppc-dev
In-Reply-To: <20141008025210.AF949140144@ozlabs.org>

Sorry it took me so long to get back to this...

On 10/07/2014 09:52 PM, Michael Ellerman wrote:
> On Tue, 2014-07-10 at 19:13:24 UTC, Paul Clarke wrote:
>> The POWER ISA defines an always-running decrementer which can be used
>> to schedule interrupts after a certain time interval has elapsed.
>> The decrementer counts down at the same frequency as the Time Base,
>> which is 512 MHz.  The maximum value of the decrementer is 0x7fffffff.
>> This works out to a maximum interval of about 4.19 seconds.
>>
>> If a larger interval is desired, the kernel will set the decrementer
>> to its maximum value and reset it after it expires (underflows)
>> a sufficient number of times until the desired interval has elapsed.
>>
>> The negative effect of this is that an unwanted latency spike will
>> impact normal processing at most every 4.19 seconds.  On an IBM
>> POWER8-based system, this spike was measured at about 25-30
>> microseconds, much of which was basic, opportunistic housekeeping
>> tasks that could otherwise have waited.
>>
>> This patch short-circuits the reset of the decrementer, exiting after
>> the decrementer reset, but before the housekeeping tasks if the only
>> need for the interrupt is simply to reset it.  After this patch,
>> the latency spike was measured at about 150 nanoseconds.

> Thanks for the excellent changelog. But this patch makes me a bit nervous :)
>
> Do you know where the latency is coming from? Is it primarily the irq work?

Yes, it is all under irq_enter (measured at ~10us) and irq_exit (~12us).

> If so I'd prefer if we could move the short circuit into __timer_interrupt()
> itself. That way we'd still have the trace points usable, and it would
> hopefully result in less duplicated logic.

But irq_enter and irq_exit are called in timer_interrupt, before 
__timer_interrupt is called.  I don't see how that helps.  The time 
spent in __timer_interrupt is minuscule by comparison.

Are you suggesting that irq_enter/exit be moved into __timer_interrupt 
as well?  (I'm not sure how that would impact the existing call to 
__timer_interrupt from tick_broadcast_ipi_handler?  And if there is no 
impact, what's the point of separating timer_interrupt and 
__timer_interrupt?)

Regards,
PC

^ permalink raw reply

* [PATCH] macintosh: therm_pm72: delete deprecated driver
From: Wolfram Sang @ 2014-11-06  1:45 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Wolfram Sang

The new driver is around for more than 2 years now, so the old one can
go. Getting rid of it helps the removal of the legacy .attach_adapter
callback of the I2C subsystem.

Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
---
 drivers/macintosh/Kconfig      |   10 -
 drivers/macintosh/Makefile     |    1 -
 drivers/macintosh/therm_pm72.c | 2279 ----------------------------------------
 drivers/macintosh/therm_pm72.h |  326 ------
 4 files changed, 2616 deletions(-)
 delete mode 100644 drivers/macintosh/therm_pm72.c
 delete mode 100644 drivers/macintosh/therm_pm72.h

diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index 3067d56b11a6..5844b80bd90e 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -204,16 +204,6 @@ config THERM_ADT746X
           iBook G4, and the ATI based aluminium PowerBooks, allowing slightly
 	  better fan behaviour by default, and some manual control.
 
-config THERM_PM72
-	tristate "Support for thermal management on PowerMac G5 (AGP)"
-	depends on I2C && I2C_POWERMAC && PPC_PMAC64
-	default n
-	help
-	  This driver provides thermostat and fan control for the desktop
-	  G5 machines.
-
-	  This is deprecated, use windfarm instead.
-
 config WINDFARM
 	tristate "New PowerMac thermal control infrastructure"
 	depends on PPC
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index d2f0120bc878..383ba920085b 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -25,7 +25,6 @@ obj-$(CONFIG_ADB_IOP)		+= adb-iop.o
 obj-$(CONFIG_ADB_PMU68K)	+= via-pmu68k.o
 obj-$(CONFIG_ADB_MACIO)		+= macio-adb.o
 
-obj-$(CONFIG_THERM_PM72)	+= therm_pm72.o
 obj-$(CONFIG_THERM_WINDTUNNEL)	+= therm_windtunnel.o
 obj-$(CONFIG_THERM_ADT746X)	+= therm_adt746x.o
 obj-$(CONFIG_WINDFARM)	        += windfarm_core.o
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
deleted file mode 100644
index 97cfc5ac9fd0..000000000000
--- a/drivers/macintosh/therm_pm72.c
+++ /dev/null
@@ -1,2279 +0,0 @@
-/*
- * Device driver for the thermostats & fan controller of  the
- * Apple G5 "PowerMac7,2" desktop machines.
- *
- * (c) Copyright IBM Corp. 2003-2004
- *
- * Maintained by: Benjamin Herrenschmidt
- *                <benh@kernel.crashing.org>
- * 
- *
- * 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 7.0 snapshot property lists.
- *
- * As far as the CPUs control loops are concerned, I use the
- * calibration & PID constants provided by the EEPROM,
- * I do _not_ embed any value from the property lists, as the ones
- * provided by Darwin 7.0 seem to always have an older version that
- * what I've seen on the actual computers.
- * It would be interesting to verify that though. Darwin has a
- * version code of 1.0.0d11 for all control loops it seems, while
- * so far, the machines EEPROMs contain a dataset versioned 1.0.0f
- *
- * Darwin doesn't provide source to all parts, some missing
- * bits like the AppleFCU driver or the actual scale of some
- * of the values returned by sensors had to be "guessed" some
- * way... or based on what Open Firmware does.
- *
- * I didn't yet figure out how to get the slots power consumption
- * out of the FCU, so that part has not been implemented yet and
- * the slots fan is set to a fixed 50% PWM, hoping this value is
- * safe enough ...
- *
- * Note: I have observed strange oscillations of the CPU control
- * loop on a dual G5 here. When idle, the CPU exhaust fan tend to
- * oscillates slowly (over several minutes) between the minimum
- * of 300RPMs and approx. 1000 RPMs. I don't know what is causing
- * this, it could be some incorrect constant or an error in the
- * way I ported the algorithm, or it could be just normal. I
- * don't have full understanding on the way Apple tweaked the PID
- * algorithm for the CPU control, it is definitely not a standard
- * implementation...
- *
- * TODO:  - Check MPU structure version/signature
- *        - Add things like /sbin/overtemp for non-critical
- *          overtemp conditions so userland can take some policy
- *          decisions, like slowing down CPUs
- *	  - Deal with fan and i2c failures in a better way
- *	  - Maybe do a generic PID based on params used for
- *	    U3 and Drives ? Definitely need to factor code a bit
- *          better... also make sensor detection more robust using
- *          the device-tree to probe for them
- *        - Figure out how to get the slots consumption and set the
- *          slots fan accordingly
- *
- * History:
- *
- *  Nov. 13, 2003 : 0.5
- *	- First release
- *
- *  Nov. 14, 2003 : 0.6
- *	- Read fan speed from FCU, low level fan routines now deal
- *	  with errors & check fan status, though higher level don't
- *	  do much.
- *	- Move a bunch of definitions to .h file
- *
- *  Nov. 18, 2003 : 0.7
- *	- Fix build on ppc64 kernel
- *	- Move back statics definitions to .c file
- *	- Avoid calling schedule_timeout with a negative number
- *
- *  Dec. 18, 2003 : 0.8
- *	- Fix typo when reading back fan speed on 2 CPU machines
- *
- *  Mar. 11, 2004 : 0.9
- *	- Rework code accessing the ADC chips, make it more robust and
- *	  closer to the chip spec. Also make sure it is configured properly,
- *        I've seen yet unexplained cases where on startup, I would have stale
- *        values in the configuration register
- *	- Switch back to use of target fan speed for PID, thus lowering
- *        pressure on i2c
- *
- *  Oct. 20, 2004 : 1.1
- *	- Add device-tree lookup for fan IDs, should detect liquid cooling
- *        pumps when present
- *	- Enable driver for PowerMac7,3 machines
- *	- Split the U3/Backside cooling on U3 & U3H versions as Darwin does
- *	- Add new CPU cooling algorithm for machines with liquid cooling
- *	- Workaround for some PowerMac7,3 with empty "fan" node in the devtree
- *	- Fix a signed/unsigned compare issue in some PID loops
- *
- *  Mar. 10, 2005 : 1.2
- *	- Add basic support for Xserve G5
- *	- Retrieve pumps min/max from EEPROM image in device-tree (broken)
- *	- Use min/max macros here or there
- *	- Latest darwin updated U3H min fan speed to 20% PWM
- *
- *  July. 06, 2006 : 1.3
- *	- Fix setting of RPM fans on Xserve G5 (they were going too fast)
- *      - Add missing slots fan control loop for Xserve G5
- *	- Lower fixed slots fan speed from 50% to 40% on desktop G5s. We
- *        still can't properly implement the control loop for these, so let's
- *        reduce the noise a little bit, it appears that 40% still gives us
- *        a pretty good air flow
- *	- Add code to "tickle" the FCU regulary so it doesn't think that
- *        we are gone while in fact, the machine just didn't need any fan
- *        speed change lately
- *
- */
-
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/wait.h>
-#include <linux/reboot.h>
-#include <linux/kmod.h>
-#include <linux/i2c.h>
-#include <linux/kthread.h>
-#include <linux/mutex.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <asm/prom.h>
-#include <asm/machdep.h>
-#include <asm/io.h>
-#include <asm/sections.h>
-#include <asm/macio.h>
-
-#include "therm_pm72.h"
-
-#define VERSION "1.3"
-
-#undef DEBUG
-
-#ifdef DEBUG
-#define DBG(args...)	printk(args)
-#else
-#define DBG(args...)	do { } while(0)
-#endif
-
-
-/*
- * Driver statics
- */
-
-static struct platform_device *		of_dev;
-static struct i2c_adapter *		u3_0;
-static struct i2c_adapter *		u3_1;
-static struct i2c_adapter *		k2;
-static struct i2c_client *		fcu;
-static struct cpu_pid_state		processor_state[2];
-static struct basckside_pid_params	backside_params;
-static struct backside_pid_state	backside_state;
-static struct drives_pid_state		drives_state;
-static struct dimm_pid_state		dimms_state;
-static struct slots_pid_state		slots_state;
-static int				state;
-static int				cpu_count;
-static int				cpu_pid_type;
-static struct task_struct		*ctrl_task;
-static struct completion		ctrl_complete;
-static int				critical_state;
-static int				rackmac;
-static s32				dimm_output_clamp;
-static int 				fcu_rpm_shift;
-static int				fcu_tickle_ticks;
-static DEFINE_MUTEX(driver_lock);
-
-/*
- * We have 3 types of CPU PID control. One is "split" old style control
- * for intake & exhaust fans, the other is "combined" control for both
- * CPUs that also deals with the pumps when present. To be "compatible"
- * with OS X at this point, we only use "COMBINED" on the machines that
- * are identified as having the pumps (though that identification is at
- * least dodgy). Ultimately, we could probably switch completely to this
- * algorithm provided we hack it to deal with the UP case
- */
-#define CPU_PID_TYPE_SPLIT	0
-#define CPU_PID_TYPE_COMBINED	1
-#define CPU_PID_TYPE_RACKMAC	2
-
-/*
- * This table describes all fans in the FCU. The "id" and "type" values
- * are defaults valid for all earlier machines. Newer machines will
- * eventually override the table content based on the device-tree
- */
-struct fcu_fan_table
-{
-	char*	loc;	/* location code */
-	int	type;	/* 0 = rpm, 1 = pwm, 2 = pump */
-	int	id;	/* id or -1 */
-};
-
-#define FCU_FAN_RPM		0
-#define FCU_FAN_PWM		1
-
-#define FCU_FAN_ABSENT_ID	-1
-
-#define FCU_FAN_COUNT		ARRAY_SIZE(fcu_fans)
-
-struct fcu_fan_table	fcu_fans[] = {
-	[BACKSIDE_FAN_PWM_INDEX] = {
-		.loc	= "BACKSIDE,SYS CTRLR FAN",
-		.type	= FCU_FAN_PWM,
-		.id	= BACKSIDE_FAN_PWM_DEFAULT_ID,
-	},
-	[DRIVES_FAN_RPM_INDEX] = {
-		.loc	= "DRIVE BAY",
-		.type	= FCU_FAN_RPM,
-		.id	= DRIVES_FAN_RPM_DEFAULT_ID,
-	},
-	[SLOTS_FAN_PWM_INDEX] = {
-		.loc	= "SLOT,PCI FAN",
-		.type	= FCU_FAN_PWM,
-		.id	= SLOTS_FAN_PWM_DEFAULT_ID,
-	},
-	[CPUA_INTAKE_FAN_RPM_INDEX] = {
-		.loc	= "CPU A INTAKE",
-		.type	= FCU_FAN_RPM,
-		.id	= CPUA_INTAKE_FAN_RPM_DEFAULT_ID,
-	},
-	[CPUA_EXHAUST_FAN_RPM_INDEX] = {
-		.loc	= "CPU A EXHAUST",
-		.type	= FCU_FAN_RPM,
-		.id	= CPUA_EXHAUST_FAN_RPM_DEFAULT_ID,
-	},
-	[CPUB_INTAKE_FAN_RPM_INDEX] = {
-		.loc	= "CPU B INTAKE",
-		.type	= FCU_FAN_RPM,
-		.id	= CPUB_INTAKE_FAN_RPM_DEFAULT_ID,
-	},
-	[CPUB_EXHAUST_FAN_RPM_INDEX] = {
-		.loc	= "CPU B EXHAUST",
-		.type	= FCU_FAN_RPM,
-		.id	= CPUB_EXHAUST_FAN_RPM_DEFAULT_ID,
-	},
-	/* pumps aren't present by default, have to be looked up in the
-	 * device-tree
-	 */
-	[CPUA_PUMP_RPM_INDEX] = {
-		.loc	= "CPU A PUMP",
-		.type	= FCU_FAN_RPM,		
-		.id	= FCU_FAN_ABSENT_ID,
-	},
-	[CPUB_PUMP_RPM_INDEX] = {
-		.loc	= "CPU B PUMP",
-		.type	= FCU_FAN_RPM,
-		.id	= FCU_FAN_ABSENT_ID,
-	},
-	/* Xserve fans */
-	[CPU_A1_FAN_RPM_INDEX] = {
-		.loc	= "CPU A 1",
-		.type	= FCU_FAN_RPM,
-		.id	= FCU_FAN_ABSENT_ID,
-	},
-	[CPU_A2_FAN_RPM_INDEX] = {
-		.loc	= "CPU A 2",
-		.type	= FCU_FAN_RPM,
-		.id	= FCU_FAN_ABSENT_ID,
-	},
-	[CPU_A3_FAN_RPM_INDEX] = {
-		.loc	= "CPU A 3",
-		.type	= FCU_FAN_RPM,
-		.id	= FCU_FAN_ABSENT_ID,
-	},
-	[CPU_B1_FAN_RPM_INDEX] = {
-		.loc	= "CPU B 1",
-		.type	= FCU_FAN_RPM,
-		.id	= FCU_FAN_ABSENT_ID,
-	},
-	[CPU_B2_FAN_RPM_INDEX] = {
-		.loc	= "CPU B 2",
-		.type	= FCU_FAN_RPM,
-		.id	= FCU_FAN_ABSENT_ID,
-	},
-	[CPU_B3_FAN_RPM_INDEX] = {
-		.loc	= "CPU B 3",
-		.type	= FCU_FAN_RPM,
-		.id	= FCU_FAN_ABSENT_ID,
-	},
-};
-
-static struct i2c_driver therm_pm72_driver;
-
-/*
- * Utility function to create an i2c_client structure and
- * attach it to one of u3 adapters
- */
-static struct i2c_client *attach_i2c_chip(int id, const char *name)
-{
-	struct i2c_client *clt;
-	struct i2c_adapter *adap;
-	struct i2c_board_info info;
-
-	if (id & 0x200)
-		adap = k2;
-	else if (id & 0x100)
-		adap = u3_1;
-	else
-		adap = u3_0;
-	if (adap == NULL)
-		return NULL;
-
-	memset(&info, 0, sizeof(struct i2c_board_info));
-	info.addr = (id >> 1) & 0x7f;
-	strlcpy(info.type, "therm_pm72", I2C_NAME_SIZE);
-	clt = i2c_new_device(adap, &info);
-	if (!clt) {
-		printk(KERN_ERR "therm_pm72: Failed to attach to i2c ID 0x%x\n", id);
-		return NULL;
-	}
-
-	/*
-	 * Let i2c-core delete that device on driver removal.
-	 * This is safe because i2c-core holds the core_lock mutex for us.
-	 */
-	list_add_tail(&clt->detected, &therm_pm72_driver.clients);
-	return clt;
-}
-
-/*
- * Here are the i2c chip access wrappers
- */
-
-static void initialize_adc(struct cpu_pid_state *state)
-{
-	int rc;
-	u8 buf[2];
-
-	/* Read ADC the configuration register and cache it. We
-	 * also make sure Config2 contains proper values, I've seen
-	 * cases where we got stale grabage in there, thus preventing
-	 * proper reading of conv. values
-	 */
-
-	/* Clear Config2 */
-	buf[0] = 5;
-	buf[1] = 0;
-	i2c_master_send(state->monitor, buf, 2);
-
-	/* Read & cache Config1 */
-	buf[0] = 1;
-	rc = i2c_master_send(state->monitor, buf, 1);
-	if (rc > 0) {
-		rc = i2c_master_recv(state->monitor, buf, 1);
-		if (rc > 0) {
-			state->adc_config = buf[0];
-			DBG("ADC config reg: %02x\n", state->adc_config);
-			/* Disable shutdown mode */
-		       	state->adc_config &= 0xfe;
-			buf[0] = 1;
-			buf[1] = state->adc_config;
-			rc = i2c_master_send(state->monitor, buf, 2);
-		}
-	}
-	if (rc <= 0)
-		printk(KERN_ERR "therm_pm72: Error reading ADC config"
-		       " register !\n");
-}
-
-static int read_smon_adc(struct cpu_pid_state *state, int chan)
-{
-	int rc, data, tries = 0;
-	u8 buf[2];
-
-	for (;;) {
-		/* Set channel */
-		buf[0] = 1;
-		buf[1] = (state->adc_config & 0x1f) | (chan << 5);
-		rc = i2c_master_send(state->monitor, buf, 2);
-		if (rc <= 0)
-			goto error;
-		/* Wait for conversion */
-		msleep(1);
-		/* Switch to data register */
-		buf[0] = 4;
-		rc = i2c_master_send(state->monitor, buf, 1);
-		if (rc <= 0)
-			goto error;
-		/* Read result */
-		rc = i2c_master_recv(state->monitor, buf, 2);
-		if (rc < 0)
-			goto error;
-		data = ((u16)buf[0]) << 8 | (u16)buf[1];
-		return data >> 6;
-	error:
-		DBG("Error reading ADC, retrying...\n");
-		if (++tries > 10) {
-			printk(KERN_ERR "therm_pm72: Error reading ADC !\n");
-			return -1;
-		}
-		msleep(10);
-	}
-}
-
-static int read_lm87_reg(struct i2c_client * chip, int reg)
-{
-	int rc, tries = 0;
-	u8 buf;
-
-	for (;;) {
-		/* Set address */
-		buf = (u8)reg;
-		rc = i2c_master_send(chip, &buf, 1);
-		if (rc <= 0)
-			goto error;
-		rc = i2c_master_recv(chip, &buf, 1);
-		if (rc <= 0)
-			goto error;
-		return (int)buf;
-	error:
-		DBG("Error reading LM87, retrying...\n");
-		if (++tries > 10) {
-			printk(KERN_ERR "therm_pm72: Error reading LM87 !\n");
-			return -1;
-		}
-		msleep(10);
-	}
-}
-
-static int fan_read_reg(int reg, unsigned char *buf, int nb)
-{
-	int tries, nr, nw;
-
-	buf[0] = reg;
-	tries = 0;
-	for (;;) {
-		nw = i2c_master_send(fcu, buf, 1);
-		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
-			break;
-		msleep(10);
-		++tries;
-	}
-	if (nw <= 0) {
-		printk(KERN_ERR "Failure writing address to FCU: %d", nw);
-		return -EIO;
-	}
-	tries = 0;
-	for (;;) {
-		nr = i2c_master_recv(fcu, buf, nb);
-		if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100)
-			break;
-		msleep(10);
-		++tries;
-	}
-	if (nr <= 0)
-		printk(KERN_ERR "Failure reading data from FCU: %d", nw);
-	return nr;
-}
-
-static int fan_write_reg(int reg, const unsigned char *ptr, int nb)
-{
-	int tries, nw;
-	unsigned char buf[16];
-
-	buf[0] = reg;
-	memcpy(buf+1, ptr, nb);
-	++nb;
-	tries = 0;
-	for (;;) {
-		nw = i2c_master_send(fcu, buf, nb);
-		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
-			break;
-		msleep(10);
-		++tries;
-	}
-	if (nw < 0)
-		printk(KERN_ERR "Failure writing to FCU: %d", nw);
-	return nw;
-}
-
-static int start_fcu(void)
-{
-	unsigned char buf = 0xff;
-	int rc;
-
-	rc = fan_write_reg(0xe, &buf, 1);
-	if (rc < 0)
-		return -EIO;
-	rc = fan_write_reg(0x2e, &buf, 1);
-	if (rc < 0)
-		return -EIO;
-	rc = fan_read_reg(0, &buf, 1);
-	if (rc < 0)
-		return -EIO;
-	fcu_rpm_shift = (buf == 1) ? 2 : 3;
-	printk(KERN_DEBUG "FCU Initialized, RPM fan shift is %d\n",
-	       fcu_rpm_shift);
-
-	return 0;
-}
-
-static int set_rpm_fan(int fan_index, int rpm)
-{
-	unsigned char buf[2];
-	int rc, id, min, max;
-
-	if (fcu_fans[fan_index].type != FCU_FAN_RPM)
-		return -EINVAL;
-	id = fcu_fans[fan_index].id; 
-	if (id == FCU_FAN_ABSENT_ID)
-		return -EINVAL;
-
-	min = 2400 >> fcu_rpm_shift;
-	max = 56000 >> fcu_rpm_shift;
-
-	if (rpm < min)
-		rpm = min;
-	else if (rpm > max)
-		rpm = max;
-	buf[0] = rpm >> (8 - fcu_rpm_shift);
-	buf[1] = rpm << fcu_rpm_shift;
-	rc = fan_write_reg(0x10 + (id * 2), buf, 2);
-	if (rc < 0)
-		return -EIO;
-	return 0;
-}
-
-static int get_rpm_fan(int fan_index, int programmed)
-{
-	unsigned char failure;
-	unsigned char active;
-	unsigned char buf[2];
-	int rc, id, reg_base;
-
-	if (fcu_fans[fan_index].type != FCU_FAN_RPM)
-		return -EINVAL;
-	id = fcu_fans[fan_index].id; 
-	if (id == FCU_FAN_ABSENT_ID)
-		return -EINVAL;
-
-	rc = fan_read_reg(0xb, &failure, 1);
-	if (rc != 1)
-		return -EIO;
-	if ((failure & (1 << id)) != 0)
-		return -EFAULT;
-	rc = fan_read_reg(0xd, &active, 1);
-	if (rc != 1)
-		return -EIO;
-	if ((active & (1 << id)) == 0)
-		return -ENXIO;
-
-	/* Programmed value or real current speed */
-	reg_base = programmed ? 0x10 : 0x11;
-	rc = fan_read_reg(reg_base + (id * 2), buf, 2);
-	if (rc != 2)
-		return -EIO;
-
-	return (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift;
-}
-
-static int set_pwm_fan(int fan_index, int pwm)
-{
-	unsigned char buf[2];
-	int rc, id;
-
-	if (fcu_fans[fan_index].type != FCU_FAN_PWM)
-		return -EINVAL;
-	id = fcu_fans[fan_index].id; 
-	if (id == FCU_FAN_ABSENT_ID)
-		return -EINVAL;
-
-	if (pwm < 10)
-		pwm = 10;
-	else if (pwm > 100)
-		pwm = 100;
-	pwm = (pwm * 2559) / 1000;
-	buf[0] = pwm;
-	rc = fan_write_reg(0x30 + (id * 2), buf, 1);
-	if (rc < 0)
-		return rc;
-	return 0;
-}
-
-static int get_pwm_fan(int fan_index)
-{
-	unsigned char failure;
-	unsigned char active;
-	unsigned char buf[2];
-	int rc, id;
-
-	if (fcu_fans[fan_index].type != FCU_FAN_PWM)
-		return -EINVAL;
-	id = fcu_fans[fan_index].id; 
-	if (id == FCU_FAN_ABSENT_ID)
-		return -EINVAL;
-
-	rc = fan_read_reg(0x2b, &failure, 1);
-	if (rc != 1)
-		return -EIO;
-	if ((failure & (1 << id)) != 0)
-		return -EFAULT;
-	rc = fan_read_reg(0x2d, &active, 1);
-	if (rc != 1)
-		return -EIO;
-	if ((active & (1 << id)) == 0)
-		return -ENXIO;
-
-	/* Programmed value or real current speed */
-	rc = fan_read_reg(0x30 + (id * 2), buf, 1);
-	if (rc != 1)
-		return -EIO;
-
-	return (buf[0] * 1000) / 2559;
-}
-
-static void tickle_fcu(void)
-{
-	int pwm;
-
-	pwm = get_pwm_fan(SLOTS_FAN_PWM_INDEX);
-
-	DBG("FCU Tickle, slots fan is: %d\n", pwm);
-	if (pwm < 0)
-		pwm = 100;
-
-	if (!rackmac) {
-		pwm = SLOTS_FAN_DEFAULT_PWM;
-	} else if (pwm < SLOTS_PID_OUTPUT_MIN)
-		pwm = SLOTS_PID_OUTPUT_MIN;
-
-	/* That is hopefully enough to make the FCU happy */
-	set_pwm_fan(SLOTS_FAN_PWM_INDEX, pwm);
-}
-
-
-/*
- * Utility routine to read the CPU calibration EEPROM data
- * from the device-tree
- */
-static int read_eeprom(int cpu, struct mpu_data *out)
-{
-	struct device_node *np;
-	char nodename[64];
-	const u8 *data;
-	int len;
-
-	/* prom.c routine for finding a node by path is a bit brain dead
-	 * and requires exact @xxx unit numbers. This is a bit ugly but
-	 * will work for these machines
-	 */
-	sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0);
-	np = of_find_node_by_path(nodename);
-	if (np == NULL) {
-		printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid node from device-tree\n");
-		return -ENODEV;
-	}
-	data = of_get_property(np, "cpuid", &len);
-	if (data == NULL) {
-		printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid property from device-tree\n");
-		of_node_put(np);
-		return -ENODEV;
-	}
-	memcpy(out, data, sizeof(struct mpu_data));
-	of_node_put(np);
-	
-	return 0;
-}
-
-static void fetch_cpu_pumps_minmax(void)
-{
-	struct cpu_pid_state *state0 = &processor_state[0];
-	struct cpu_pid_state *state1 = &processor_state[1];
-	u16 pump_min = 0, pump_max = 0xffff;
-	u16 tmp[4];
-
-	/* Try to fetch pumps min/max infos from eeprom */
-
-	memcpy(&tmp, &state0->mpu.processor_part_num, 8);
-	if (tmp[0] != 0xffff && tmp[1] != 0xffff) {
-		pump_min = max(pump_min, tmp[0]);
-		pump_max = min(pump_max, tmp[1]);
-	}
-	if (tmp[2] != 0xffff && tmp[3] != 0xffff) {
-		pump_min = max(pump_min, tmp[2]);
-		pump_max = min(pump_max, tmp[3]);
-	}
-
-	/* Double check the values, this _IS_ needed as the EEPROM on
-	 * some dual 2.5Ghz G5s seem, at least, to have both min & max
-	 * same to the same value ... (grrrr)
-	 */
-	if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) {
-		pump_min = CPU_PUMP_OUTPUT_MIN;
-		pump_max = CPU_PUMP_OUTPUT_MAX;
-	}
-
-	state0->pump_min = state1->pump_min = pump_min;
-	state0->pump_max = state1->pump_max = pump_max;
-}
-
-/* 
- * Now, unfortunately, sysfs doesn't give us a nice void * we could
- * pass around to the attribute functions, so we don't really have
- * choice but implement a bunch of them...
- *
- * That sucks a bit, we take the lock because FIX32TOPRINT evaluates
- * the input twice... I accept patches :)
- */
-#define BUILD_SHOW_FUNC_FIX(name, data)				\
-static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)	\
-{								\
-	ssize_t r;						\
-	mutex_lock(&driver_lock);					\
-	r = sprintf(buf, "%d.%03d", FIX32TOPRINT(data));	\
-	mutex_unlock(&driver_lock);					\
-	return r;						\
-}
-#define BUILD_SHOW_FUNC_INT(name, data)				\
-static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)	\
-{								\
-	return sprintf(buf, "%d", data);			\
-}
-
-BUILD_SHOW_FUNC_FIX(cpu0_temperature, processor_state[0].last_temp)
-BUILD_SHOW_FUNC_FIX(cpu0_voltage, processor_state[0].voltage)
-BUILD_SHOW_FUNC_FIX(cpu0_current, processor_state[0].current_a)
-BUILD_SHOW_FUNC_INT(cpu0_exhaust_fan_rpm, processor_state[0].rpm)
-BUILD_SHOW_FUNC_INT(cpu0_intake_fan_rpm, processor_state[0].intake_rpm)
-
-BUILD_SHOW_FUNC_FIX(cpu1_temperature, processor_state[1].last_temp)
-BUILD_SHOW_FUNC_FIX(cpu1_voltage, processor_state[1].voltage)
-BUILD_SHOW_FUNC_FIX(cpu1_current, processor_state[1].current_a)
-BUILD_SHOW_FUNC_INT(cpu1_exhaust_fan_rpm, processor_state[1].rpm)
-BUILD_SHOW_FUNC_INT(cpu1_intake_fan_rpm, processor_state[1].intake_rpm)
-
-BUILD_SHOW_FUNC_FIX(backside_temperature, backside_state.last_temp)
-BUILD_SHOW_FUNC_INT(backside_fan_pwm, backside_state.pwm)
-
-BUILD_SHOW_FUNC_FIX(drives_temperature, drives_state.last_temp)
-BUILD_SHOW_FUNC_INT(drives_fan_rpm, drives_state.rpm)
-
-BUILD_SHOW_FUNC_FIX(slots_temperature, slots_state.last_temp)
-BUILD_SHOW_FUNC_INT(slots_fan_pwm, slots_state.pwm)
-
-BUILD_SHOW_FUNC_FIX(dimms_temperature, dimms_state.last_temp)
-
-static DEVICE_ATTR(cpu0_temperature,S_IRUGO,show_cpu0_temperature,NULL);
-static DEVICE_ATTR(cpu0_voltage,S_IRUGO,show_cpu0_voltage,NULL);
-static DEVICE_ATTR(cpu0_current,S_IRUGO,show_cpu0_current,NULL);
-static DEVICE_ATTR(cpu0_exhaust_fan_rpm,S_IRUGO,show_cpu0_exhaust_fan_rpm,NULL);
-static DEVICE_ATTR(cpu0_intake_fan_rpm,S_IRUGO,show_cpu0_intake_fan_rpm,NULL);
-
-static DEVICE_ATTR(cpu1_temperature,S_IRUGO,show_cpu1_temperature,NULL);
-static DEVICE_ATTR(cpu1_voltage,S_IRUGO,show_cpu1_voltage,NULL);
-static DEVICE_ATTR(cpu1_current,S_IRUGO,show_cpu1_current,NULL);
-static DEVICE_ATTR(cpu1_exhaust_fan_rpm,S_IRUGO,show_cpu1_exhaust_fan_rpm,NULL);
-static DEVICE_ATTR(cpu1_intake_fan_rpm,S_IRUGO,show_cpu1_intake_fan_rpm,NULL);
-
-static DEVICE_ATTR(backside_temperature,S_IRUGO,show_backside_temperature,NULL);
-static DEVICE_ATTR(backside_fan_pwm,S_IRUGO,show_backside_fan_pwm,NULL);
-
-static DEVICE_ATTR(drives_temperature,S_IRUGO,show_drives_temperature,NULL);
-static DEVICE_ATTR(drives_fan_rpm,S_IRUGO,show_drives_fan_rpm,NULL);
-
-static DEVICE_ATTR(slots_temperature,S_IRUGO,show_slots_temperature,NULL);
-static DEVICE_ATTR(slots_fan_pwm,S_IRUGO,show_slots_fan_pwm,NULL);
-
-static DEVICE_ATTR(dimms_temperature,S_IRUGO,show_dimms_temperature,NULL);
-
-/*
- * CPUs fans control loop
- */
-
-static int do_read_one_cpu_values(struct cpu_pid_state *state, s32 *temp, s32 *power)
-{
-	s32 ltemp, volts, amps;
-	int index, rc = 0;
-
-	/* Default (in case of error) */
-	*temp = state->cur_temp;
-	*power = state->cur_power;
-
-	if (cpu_pid_type == CPU_PID_TYPE_RACKMAC)
-		index = (state->index == 0) ?
-			CPU_A1_FAN_RPM_INDEX : CPU_B1_FAN_RPM_INDEX;
-	else
-		index = (state->index == 0) ?
-			CPUA_EXHAUST_FAN_RPM_INDEX : CPUB_EXHAUST_FAN_RPM_INDEX;
-
-	/* Read current fan status */
-	rc = get_rpm_fan(index, !RPM_PID_USE_ACTUAL_SPEED);
-	if (rc < 0) {
-		/* XXX What do we do now ? Nothing for now, keep old value, but
-		 * return error upstream
-		 */
-		DBG("  cpu %d, fan reading error !\n", state->index);
-	} else {
-		state->rpm = rc;
-		DBG("  cpu %d, exhaust RPM: %d\n", state->index, state->rpm);
-	}
-
-	/* Get some sensor readings and scale it */
-	ltemp = read_smon_adc(state, 1);
-	if (ltemp == -1) {
-		/* XXX What do we do now ? */
-		state->overtemp++;
-		if (rc == 0)
-			rc = -EIO;
-		DBG("  cpu %d, temp reading error !\n", state->index);
-	} else {
-		/* Fixup temperature according to diode calibration
-		 */
-		DBG("  cpu %d, temp raw: %04x, m_diode: %04x, b_diode: %04x\n",
-		    state->index,
-		    ltemp, state->mpu.mdiode, state->mpu.bdiode);
-		*temp = ((s32)ltemp * (s32)state->mpu.mdiode + ((s32)state->mpu.bdiode << 12)) >> 2;
-		state->last_temp = *temp;
-		DBG("  temp: %d.%03d\n", FIX32TOPRINT((*temp)));
-	}
-
-	/*
-	 * Read voltage & current and calculate power
-	 */
-	volts = read_smon_adc(state, 3);
-	amps = read_smon_adc(state, 4);
-
-	/* Scale voltage and current raw sensor values according to fixed scales
-	 * obtained in Darwin and calculate power from I and V
-	 */
-	volts *= ADC_CPU_VOLTAGE_SCALE;
-	amps *= ADC_CPU_CURRENT_SCALE;
-	*power = (((u64)volts) * ((u64)amps)) >> 16;
-	state->voltage = volts;
-	state->current_a = amps;
-	state->last_power = *power;
-
-	DBG("  cpu %d, current: %d.%03d, voltage: %d.%03d, power: %d.%03d W\n",
-	    state->index, FIX32TOPRINT(state->current_a),
-	    FIX32TOPRINT(state->voltage), FIX32TOPRINT(*power));
-
-	return 0;
-}
-
-static void do_cpu_pid(struct cpu_pid_state *state, s32 temp, s32 power)
-{
-	s32 power_target, integral, derivative, proportional, adj_in_target, sval;
-	s64 integ_p, deriv_p, prop_p, sum; 
-	int i;
-
-	/* Calculate power target value (could be done once for all)
-	 * and convert to a 16.16 fp number
-	 */
-	power_target = ((u32)(state->mpu.pmaxh - state->mpu.padjmax)) << 16;
-	DBG("  power target: %d.%03d, error: %d.%03d\n",
-	    FIX32TOPRINT(power_target), FIX32TOPRINT(power_target - power));
-
-	/* Store temperature and power in history array */
-	state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE;
-	state->temp_history[state->cur_temp] = temp;
-	state->cur_power = (state->cur_power + 1) % state->count_power;
-	state->power_history[state->cur_power] = power;
-	state->error_history[state->cur_power] = power_target - power;
-	
-	/* If first loop, fill the history table */
-	if (state->first) {
-		for (i = 0; i < (state->count_power - 1); i++) {
-			state->cur_power = (state->cur_power + 1) % state->count_power;
-			state->power_history[state->cur_power] = power;
-			state->error_history[state->cur_power] = power_target - power;
-		}
-		for (i = 0; i < (CPU_TEMP_HISTORY_SIZE - 1); i++) {
-			state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE;
-			state->temp_history[state->cur_temp] = temp;			
-		}
-		state->first = 0;
-	}
-
-	/* Calculate the integral term normally based on the "power" values */
-	sum = 0;
-	integral = 0;
-	for (i = 0; i < state->count_power; i++)
-		integral += state->error_history[i];
-	integral *= CPU_PID_INTERVAL;
-	DBG("  integral: %08x\n", integral);
-
-	/* Calculate the adjusted input (sense value).
-	 *   G_r is 12.20
-	 *   integ is 16.16
-	 *   so the result is 28.36
-	 *
-	 * input target is mpu.ttarget, input max is mpu.tmax
-	 */
-	integ_p = ((s64)state->mpu.pid_gr) * (s64)integral;
-	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
-	sval = (state->mpu.tmax << 16) - ((integ_p >> 20) & 0xffffffff);
-	adj_in_target = (state->mpu.ttarget << 16);
-	if (adj_in_target > sval)
-		adj_in_target = sval;
-	DBG("   adj_in_target: %d.%03d, ttarget: %d\n", FIX32TOPRINT(adj_in_target),
-	    state->mpu.ttarget);
-
-	/* Calculate the derivative term */
-	derivative = state->temp_history[state->cur_temp] -
-		state->temp_history[(state->cur_temp + CPU_TEMP_HISTORY_SIZE - 1)
-				    % CPU_TEMP_HISTORY_SIZE];
-	derivative /= CPU_PID_INTERVAL;
-	deriv_p = ((s64)state->mpu.pid_gd) * (s64)derivative;
-	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
-	sum += deriv_p;
-
-	/* Calculate the proportional term */
-	proportional = temp - adj_in_target;
-	prop_p = ((s64)state->mpu.pid_gp) * (s64)proportional;
-	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
-	sum += prop_p;
-
-	/* Scale sum */
-	sum >>= 36;
-
-	DBG("   sum: %d\n", (int)sum);
-	state->rpm += (s32)sum;
-}
-
-static void do_monitor_cpu_combined(void)
-{
-	struct cpu_pid_state *state0 = &processor_state[0];
-	struct cpu_pid_state *state1 = &processor_state[1];
-	s32 temp0, power0, temp1, power1;
-	s32 temp_combi, power_combi;
-	int rc, intake, pump;
-
-	rc = do_read_one_cpu_values(state0, &temp0, &power0);
-	if (rc < 0) {
-		/* XXX What do we do now ? */
-	}
-	state1->overtemp = 0;
-	rc = do_read_one_cpu_values(state1, &temp1, &power1);
-	if (rc < 0) {
-		/* XXX What do we do now ? */
-	}
-	if (state1->overtemp)
-		state0->overtemp++;
-
-	temp_combi = max(temp0, temp1);
-	power_combi = max(power0, power1);
-
-	/* Check tmax, increment overtemp if we are there. At tmax+8, we go
-	 * full blown immediately and try to trigger a shutdown
-	 */
-	if (temp_combi >= ((state0->mpu.tmax + 8) << 16)) {
-		printk(KERN_WARNING "Warning ! Temperature way above maximum (%d) !\n",
-		       temp_combi >> 16);
-		state0->overtemp += CPU_MAX_OVERTEMP / 4;
-	} else if (temp_combi > (state0->mpu.tmax << 16)) {
-		state0->overtemp++;
-		printk(KERN_WARNING "Temperature %d above max %d. overtemp %d\n",
-		       temp_combi >> 16, state0->mpu.tmax, state0->overtemp);
-	} else {
-		if (state0->overtemp)
-			printk(KERN_WARNING "Temperature back down to %d\n",
-			       temp_combi >> 16);
-		state0->overtemp = 0;
-	}
-	if (state0->overtemp >= CPU_MAX_OVERTEMP)
-		critical_state = 1;
-	if (state0->overtemp > 0) {
-		state0->rpm = state0->mpu.rmaxn_exhaust_fan;
-		state0->intake_rpm = intake = state0->mpu.rmaxn_intake_fan;
-		pump = state0->pump_max;
-		goto do_set_fans;
-	}
-
-	/* Do the PID */
-	do_cpu_pid(state0, temp_combi, power_combi);
-
-	/* Range check */
-	state0->rpm = max(state0->rpm, (int)state0->mpu.rminn_exhaust_fan);
-	state0->rpm = min(state0->rpm, (int)state0->mpu.rmaxn_exhaust_fan);
-
-	/* Calculate intake fan speed */
-	intake = (state0->rpm * CPU_INTAKE_SCALE) >> 16;
-	intake = max(intake, (int)state0->mpu.rminn_intake_fan);
-	intake = min(intake, (int)state0->mpu.rmaxn_intake_fan);
-	state0->intake_rpm = intake;
-
-	/* Calculate pump speed */
-	pump = (state0->rpm * state0->pump_max) /
-		state0->mpu.rmaxn_exhaust_fan;
-	pump = min(pump, state0->pump_max);
-	pump = max(pump, state0->pump_min);
-	
- do_set_fans:
-	/* We copy values from state 0 to state 1 for /sysfs */
-	state1->rpm = state0->rpm;
-	state1->intake_rpm = state0->intake_rpm;
-
-	DBG("** CPU %d RPM: %d Ex, %d, Pump: %d, In, overtemp: %d\n",
-	    state1->index, (int)state1->rpm, intake, pump, state1->overtemp);
-
-	/* We should check for errors, shouldn't we ? But then, what
-	 * do we do once the error occurs ? For FCU notified fan
-	 * failures (-EFAULT) we probably want to notify userland
-	 * some way...
-	 */
-	set_rpm_fan(CPUA_INTAKE_FAN_RPM_INDEX, intake);
-	set_rpm_fan(CPUA_EXHAUST_FAN_RPM_INDEX, state0->rpm);
-	set_rpm_fan(CPUB_INTAKE_FAN_RPM_INDEX, intake);
-	set_rpm_fan(CPUB_EXHAUST_FAN_RPM_INDEX, state0->rpm);
-
-	if (fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID)
-		set_rpm_fan(CPUA_PUMP_RPM_INDEX, pump);
-	if (fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID)
-		set_rpm_fan(CPUB_PUMP_RPM_INDEX, pump);
-}
-
-static void do_monitor_cpu_split(struct cpu_pid_state *state)
-{
-	s32 temp, power;
-	int rc, intake;
-
-	/* Read current fan status */
-	rc = do_read_one_cpu_values(state, &temp, &power);
-	if (rc < 0) {
-		/* XXX What do we do now ? */
-	}
-
-	/* Check tmax, increment overtemp if we are there. At tmax+8, we go
-	 * full blown immediately and try to trigger a shutdown
-	 */
-	if (temp >= ((state->mpu.tmax + 8) << 16)) {
-		printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum"
-		       " (%d) !\n",
-		       state->index, temp >> 16);
-		state->overtemp += CPU_MAX_OVERTEMP / 4;
-	} else if (temp > (state->mpu.tmax << 16)) {
-		state->overtemp++;
-		printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
-		       state->index, temp >> 16, state->mpu.tmax, state->overtemp);
-	} else {
-		if (state->overtemp)
-			printk(KERN_WARNING "CPU %d temperature back down to %d\n",
-			       state->index, temp >> 16);
-		state->overtemp = 0;
-	}
-	if (state->overtemp >= CPU_MAX_OVERTEMP)
-		critical_state = 1;
-	if (state->overtemp > 0) {
-		state->rpm = state->mpu.rmaxn_exhaust_fan;
-		state->intake_rpm = intake = state->mpu.rmaxn_intake_fan;
-		goto do_set_fans;
-	}
-
-	/* Do the PID */
-	do_cpu_pid(state, temp, power);
-
-	/* Range check */
-	state->rpm = max(state->rpm, (int)state->mpu.rminn_exhaust_fan);
-	state->rpm = min(state->rpm, (int)state->mpu.rmaxn_exhaust_fan);
-
-	/* Calculate intake fan */
-	intake = (state->rpm * CPU_INTAKE_SCALE) >> 16;
-	intake = max(intake, (int)state->mpu.rminn_intake_fan);
-	intake = min(intake, (int)state->mpu.rmaxn_intake_fan);
-	state->intake_rpm = intake;
-
- do_set_fans:
-	DBG("** CPU %d RPM: %d Ex, %d In, overtemp: %d\n",
-	    state->index, (int)state->rpm, intake, state->overtemp);
-
-	/* We should check for errors, shouldn't we ? But then, what
-	 * do we do once the error occurs ? For FCU notified fan
-	 * failures (-EFAULT) we probably want to notify userland
-	 * some way...
-	 */
-	if (state->index == 0) {
-		set_rpm_fan(CPUA_INTAKE_FAN_RPM_INDEX, intake);
-		set_rpm_fan(CPUA_EXHAUST_FAN_RPM_INDEX, state->rpm);
-	} else {
-		set_rpm_fan(CPUB_INTAKE_FAN_RPM_INDEX, intake);
-		set_rpm_fan(CPUB_EXHAUST_FAN_RPM_INDEX, state->rpm);
-	}
-}
-
-static void do_monitor_cpu_rack(struct cpu_pid_state *state)
-{
-	s32 temp, power, fan_min;
-	int rc;
-
-	/* Read current fan status */
-	rc = do_read_one_cpu_values(state, &temp, &power);
-	if (rc < 0) {
-		/* XXX What do we do now ? */
-	}
-
-	/* Check tmax, increment overtemp if we are there. At tmax+8, we go
-	 * full blown immediately and try to trigger a shutdown
-	 */
-	if (temp >= ((state->mpu.tmax + 8) << 16)) {
-		printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum"
-		       " (%d) !\n",
-		       state->index, temp >> 16);
-		state->overtemp = CPU_MAX_OVERTEMP / 4;
-	} else if (temp > (state->mpu.tmax << 16)) {
-		state->overtemp++;
-		printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
-		       state->index, temp >> 16, state->mpu.tmax, state->overtemp);
-	} else {
-		if (state->overtemp)
-			printk(KERN_WARNING "CPU %d temperature back down to %d\n",
-			       state->index, temp >> 16);
-		state->overtemp = 0;
-	}
-	if (state->overtemp >= CPU_MAX_OVERTEMP)
-		critical_state = 1;
-	if (state->overtemp > 0) {
-		state->rpm = state->intake_rpm = state->mpu.rmaxn_intake_fan;
-		goto do_set_fans;
-	}
-
-	/* Do the PID */
-	do_cpu_pid(state, temp, power);
-
-	/* Check clamp from dimms */
-	fan_min = dimm_output_clamp;
-	fan_min = max(fan_min, (int)state->mpu.rminn_intake_fan);
-
-	DBG(" CPU min mpu = %d, min dimm = %d\n",
-	    state->mpu.rminn_intake_fan, dimm_output_clamp);
-
-	state->rpm = max(state->rpm, (int)fan_min);
-	state->rpm = min(state->rpm, (int)state->mpu.rmaxn_intake_fan);
-	state->intake_rpm = state->rpm;
-
- do_set_fans:
-	DBG("** CPU %d RPM: %d overtemp: %d\n",
-	    state->index, (int)state->rpm, state->overtemp);
-
-	/* We should check for errors, shouldn't we ? But then, what
-	 * do we do once the error occurs ? For FCU notified fan
-	 * failures (-EFAULT) we probably want to notify userland
-	 * some way...
-	 */
-	if (state->index == 0) {
-		set_rpm_fan(CPU_A1_FAN_RPM_INDEX, state->rpm);
-		set_rpm_fan(CPU_A2_FAN_RPM_INDEX, state->rpm);
-		set_rpm_fan(CPU_A3_FAN_RPM_INDEX, state->rpm);
-	} else {
-		set_rpm_fan(CPU_B1_FAN_RPM_INDEX, state->rpm);
-		set_rpm_fan(CPU_B2_FAN_RPM_INDEX, state->rpm);
-		set_rpm_fan(CPU_B3_FAN_RPM_INDEX, state->rpm);
-	}
-}
-
-/*
- * Initialize the state structure for one CPU control loop
- */
-static int init_processor_state(struct cpu_pid_state *state, int index)
-{
-	int err;
-
-	state->index = index;
-	state->first = 1;
-	state->rpm = (cpu_pid_type == CPU_PID_TYPE_RACKMAC) ? 4000 : 1000;
-	state->overtemp = 0;
-	state->adc_config = 0x00;
-
-
-	if (index == 0)
-		state->monitor = attach_i2c_chip(SUPPLY_MONITOR_ID, "CPU0_monitor");
-	else if (index == 1)
-		state->monitor = attach_i2c_chip(SUPPLY_MONITORB_ID, "CPU1_monitor");
-	if (state->monitor == NULL)
-		goto fail;
-
-	if (read_eeprom(index, &state->mpu))
-		goto fail;
-
-	state->count_power = state->mpu.tguardband;
-	if (state->count_power > CPU_POWER_HISTORY_SIZE) {
-		printk(KERN_WARNING "Warning ! too many power history slots\n");
-		state->count_power = CPU_POWER_HISTORY_SIZE;
-	}
-	DBG("CPU %d Using %d power history entries\n", index, state->count_power);
-
-	if (index == 0) {
-		err = device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
-		err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
-		err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_current);
-		err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
-		err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
-	} else {
-		err = device_create_file(&of_dev->dev, &dev_attr_cpu1_temperature);
-		err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_voltage);
-		err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_current);
-		err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
-		err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
-	}
-	if (err)
-		printk(KERN_WARNING "Failed to create some of the attribute"
-			"files for CPU %d\n", index);
-
-	return 0;
- fail:
-	state->monitor = NULL;
-	
-	return -ENODEV;
-}
-
-/*
- * Dispose of the state data for one CPU control loop
- */
-static void dispose_processor_state(struct cpu_pid_state *state)
-{
-	if (state->monitor == NULL)
-		return;
-
-	if (state->index == 0) {
-		device_remove_file(&of_dev->dev, &dev_attr_cpu0_temperature);
-		device_remove_file(&of_dev->dev, &dev_attr_cpu0_voltage);
-		device_remove_file(&of_dev->dev, &dev_attr_cpu0_current);
-		device_remove_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
-		device_remove_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
-	} else {
-		device_remove_file(&of_dev->dev, &dev_attr_cpu1_temperature);
-		device_remove_file(&of_dev->dev, &dev_attr_cpu1_voltage);
-		device_remove_file(&of_dev->dev, &dev_attr_cpu1_current);
-		device_remove_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
-		device_remove_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
-	}
-
-	state->monitor = NULL;
-}
-
-/*
- * Motherboard backside & U3 heatsink fan control loop
- */
-static void do_monitor_backside(struct backside_pid_state *state)
-{
-	s32 temp, integral, derivative, fan_min;
-	s64 integ_p, deriv_p, prop_p, sum; 
-	int i, rc;
-
-	if (--state->ticks != 0)
-		return;
-	state->ticks = backside_params.interval;
-
-	DBG("backside:\n");
-
-	/* Check fan status */
-	rc = get_pwm_fan(BACKSIDE_FAN_PWM_INDEX);
-	if (rc < 0) {
-		printk(KERN_WARNING "Error %d reading backside fan !\n", rc);
-		/* XXX What do we do now ? */
-	} else
-		state->pwm = rc;
-	DBG("  current pwm: %d\n", state->pwm);
-
-	/* Get some sensor readings */
-	temp = i2c_smbus_read_byte_data(state->monitor, MAX6690_EXT_TEMP) << 16;
-	state->last_temp = temp;
-	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
-	    FIX32TOPRINT(backside_params.input_target));
-
-	/* Store temperature and error in history array */
-	state->cur_sample = (state->cur_sample + 1) % BACKSIDE_PID_HISTORY_SIZE;
-	state->sample_history[state->cur_sample] = temp;
-	state->error_history[state->cur_sample] = temp - backside_params.input_target;
-	
-	/* If first loop, fill the history table */
-	if (state->first) {
-		for (i = 0; i < (BACKSIDE_PID_HISTORY_SIZE - 1); i++) {
-			state->cur_sample = (state->cur_sample + 1) %
-				BACKSIDE_PID_HISTORY_SIZE;
-			state->sample_history[state->cur_sample] = temp;
-			state->error_history[state->cur_sample] =
-				temp - backside_params.input_target;
-		}
-		state->first = 0;
-	}
-
-	/* Calculate the integral term */
-	sum = 0;
-	integral = 0;
-	for (i = 0; i < BACKSIDE_PID_HISTORY_SIZE; i++)
-		integral += state->error_history[i];
-	integral *= backside_params.interval;
-	DBG("  integral: %08x\n", integral);
-	integ_p = ((s64)backside_params.G_r) * (s64)integral;
-	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
-	sum += integ_p;
-
-	/* Calculate the derivative term */
-	derivative = state->error_history[state->cur_sample] -
-		state->error_history[(state->cur_sample + BACKSIDE_PID_HISTORY_SIZE - 1)
-				    % BACKSIDE_PID_HISTORY_SIZE];
-	derivative /= backside_params.interval;
-	deriv_p = ((s64)backside_params.G_d) * (s64)derivative;
-	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
-	sum += deriv_p;
-
-	/* Calculate the proportional term */
-	prop_p = ((s64)backside_params.G_p) * (s64)(state->error_history[state->cur_sample]);
-	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
-	sum += prop_p;
-
-	/* Scale sum */
-	sum >>= 36;
-
-	DBG("   sum: %d\n", (int)sum);
-	if (backside_params.additive)
-		state->pwm += (s32)sum;
-	else
-		state->pwm = sum;
-
-	/* Check for clamp */
-	fan_min = (dimm_output_clamp * 100) / 14000;
-	fan_min = max(fan_min, backside_params.output_min);
-
-	state->pwm = max(state->pwm, fan_min);
-	state->pwm = min(state->pwm, backside_params.output_max);
-
-	DBG("** BACKSIDE PWM: %d\n", (int)state->pwm);
-	set_pwm_fan(BACKSIDE_FAN_PWM_INDEX, state->pwm);
-}
-
-/*
- * Initialize the state structure for the backside fan control loop
- */
-static int init_backside_state(struct backside_pid_state *state)
-{
-	struct device_node *u3;
-	int u3h = 1; /* conservative by default */
-	int err;
-
-	/*
-	 * There are different PID params for machines with U3 and machines
-	 * with U3H, pick the right ones now
-	 */
-	u3 = of_find_node_by_path("/u3@0,f8000000");
-	if (u3 != NULL) {
-		const u32 *vers = of_get_property(u3, "device-rev", NULL);
-		if (vers)
-			if (((*vers) & 0x3f) < 0x34)
-				u3h = 0;
-		of_node_put(u3);
-	}
-
-	if (rackmac) {
-		backside_params.G_d = BACKSIDE_PID_RACK_G_d;
-		backside_params.input_target = BACKSIDE_PID_RACK_INPUT_TARGET;
-		backside_params.output_min = BACKSIDE_PID_U3H_OUTPUT_MIN;
-		backside_params.interval = BACKSIDE_PID_RACK_INTERVAL;
-		backside_params.G_p = BACKSIDE_PID_RACK_G_p;
-		backside_params.G_r = BACKSIDE_PID_G_r;
-		backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
-		backside_params.additive = 0;
-	} else if (u3h) {
-		backside_params.G_d = BACKSIDE_PID_U3H_G_d;
-		backside_params.input_target = BACKSIDE_PID_U3H_INPUT_TARGET;
-		backside_params.output_min = BACKSIDE_PID_U3H_OUTPUT_MIN;
-		backside_params.interval = BACKSIDE_PID_INTERVAL;
-		backside_params.G_p = BACKSIDE_PID_G_p;
-		backside_params.G_r = BACKSIDE_PID_G_r;
-		backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
-		backside_params.additive = 1;
-	} else {
-		backside_params.G_d = BACKSIDE_PID_U3_G_d;
-		backside_params.input_target = BACKSIDE_PID_U3_INPUT_TARGET;
-		backside_params.output_min = BACKSIDE_PID_U3_OUTPUT_MIN;
-		backside_params.interval = BACKSIDE_PID_INTERVAL;
-		backside_params.G_p = BACKSIDE_PID_G_p;
-		backside_params.G_r = BACKSIDE_PID_G_r;
-		backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
-		backside_params.additive = 1;
-	}
-
-	state->ticks = 1;
-	state->first = 1;
-	state->pwm = 50;
-
-	state->monitor = attach_i2c_chip(BACKSIDE_MAX_ID, "backside_temp");
-	if (state->monitor == NULL)
-		return -ENODEV;
-
-	err = device_create_file(&of_dev->dev, &dev_attr_backside_temperature);
-	err |= device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
-	if (err)
-		printk(KERN_WARNING "Failed to create attribute file(s)"
-			" for backside fan\n");
-
-	return 0;
-}
-
-/*
- * Dispose of the state data for the backside control loop
- */
-static void dispose_backside_state(struct backside_pid_state *state)
-{
-	if (state->monitor == NULL)
-		return;
-
-	device_remove_file(&of_dev->dev, &dev_attr_backside_temperature);
-	device_remove_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
-
-	state->monitor = NULL;
-}
- 
-/*
- * Drives bay fan control loop
- */
-static void do_monitor_drives(struct drives_pid_state *state)
-{
-	s32 temp, integral, derivative;
-	s64 integ_p, deriv_p, prop_p, sum; 
-	int i, rc;
-
-	if (--state->ticks != 0)
-		return;
-	state->ticks = DRIVES_PID_INTERVAL;
-
-	DBG("drives:\n");
-
-	/* Check fan status */
-	rc = get_rpm_fan(DRIVES_FAN_RPM_INDEX, !RPM_PID_USE_ACTUAL_SPEED);
-	if (rc < 0) {
-		printk(KERN_WARNING "Error %d reading drives fan !\n", rc);
-		/* XXX What do we do now ? */
-	} else
-		state->rpm = rc;
-	DBG("  current rpm: %d\n", state->rpm);
-
-	/* Get some sensor readings */
-	temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor,
-						    DS1775_TEMP)) << 8;
-	state->last_temp = temp;
-	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
-	    FIX32TOPRINT(DRIVES_PID_INPUT_TARGET));
-
-	/* Store temperature and error in history array */
-	state->cur_sample = (state->cur_sample + 1) % DRIVES_PID_HISTORY_SIZE;
-	state->sample_history[state->cur_sample] = temp;
-	state->error_history[state->cur_sample] = temp - DRIVES_PID_INPUT_TARGET;
-	
-	/* If first loop, fill the history table */
-	if (state->first) {
-		for (i = 0; i < (DRIVES_PID_HISTORY_SIZE - 1); i++) {
-			state->cur_sample = (state->cur_sample + 1) %
-				DRIVES_PID_HISTORY_SIZE;
-			state->sample_history[state->cur_sample] = temp;
-			state->error_history[state->cur_sample] =
-				temp - DRIVES_PID_INPUT_TARGET;
-		}
-		state->first = 0;
-	}
-
-	/* Calculate the integral term */
-	sum = 0;
-	integral = 0;
-	for (i = 0; i < DRIVES_PID_HISTORY_SIZE; i++)
-		integral += state->error_history[i];
-	integral *= DRIVES_PID_INTERVAL;
-	DBG("  integral: %08x\n", integral);
-	integ_p = ((s64)DRIVES_PID_G_r) * (s64)integral;
-	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
-	sum += integ_p;
-
-	/* Calculate the derivative term */
-	derivative = state->error_history[state->cur_sample] -
-		state->error_history[(state->cur_sample + DRIVES_PID_HISTORY_SIZE - 1)
-				    % DRIVES_PID_HISTORY_SIZE];
-	derivative /= DRIVES_PID_INTERVAL;
-	deriv_p = ((s64)DRIVES_PID_G_d) * (s64)derivative;
-	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
-	sum += deriv_p;
-
-	/* Calculate the proportional term */
-	prop_p = ((s64)DRIVES_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
-	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
-	sum += prop_p;
-
-	/* Scale sum */
-	sum >>= 36;
-
-	DBG("   sum: %d\n", (int)sum);
-	state->rpm += (s32)sum;
-
-	state->rpm = max(state->rpm, DRIVES_PID_OUTPUT_MIN);
-	state->rpm = min(state->rpm, DRIVES_PID_OUTPUT_MAX);
-
-	DBG("** DRIVES RPM: %d\n", (int)state->rpm);
-	set_rpm_fan(DRIVES_FAN_RPM_INDEX, state->rpm);
-}
-
-/*
- * Initialize the state structure for the drives bay fan control loop
- */
-static int init_drives_state(struct drives_pid_state *state)
-{
-	int err;
-
-	state->ticks = 1;
-	state->first = 1;
-	state->rpm = 1000;
-
-	state->monitor = attach_i2c_chip(DRIVES_DALLAS_ID, "drives_temp");
-	if (state->monitor == NULL)
-		return -ENODEV;
-
-	err = device_create_file(&of_dev->dev, &dev_attr_drives_temperature);
-	err |= device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
-	if (err)
-		printk(KERN_WARNING "Failed to create attribute file(s)"
-			" for drives bay fan\n");
-
-	return 0;
-}
-
-/*
- * Dispose of the state data for the drives control loop
- */
-static void dispose_drives_state(struct drives_pid_state *state)
-{
-	if (state->monitor == NULL)
-		return;
-
-	device_remove_file(&of_dev->dev, &dev_attr_drives_temperature);
-	device_remove_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
-
-	state->monitor = NULL;
-}
-
-/*
- * DIMMs temp control loop
- */
-static void do_monitor_dimms(struct dimm_pid_state *state)
-{
-	s32 temp, integral, derivative, fan_min;
-	s64 integ_p, deriv_p, prop_p, sum;
-	int i;
-
-	if (--state->ticks != 0)
-		return;
-	state->ticks = DIMM_PID_INTERVAL;
-
-	DBG("DIMM:\n");
-
-	DBG("  current value: %d\n", state->output);
-
-	temp = read_lm87_reg(state->monitor, LM87_INT_TEMP);
-	if (temp < 0)
-		return;
-	temp <<= 16;
-	state->last_temp = temp;
-	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
-	    FIX32TOPRINT(DIMM_PID_INPUT_TARGET));
-
-	/* Store temperature and error in history array */
-	state->cur_sample = (state->cur_sample + 1) % DIMM_PID_HISTORY_SIZE;
-	state->sample_history[state->cur_sample] = temp;
-	state->error_history[state->cur_sample] = temp - DIMM_PID_INPUT_TARGET;
-
-	/* If first loop, fill the history table */
-	if (state->first) {
-		for (i = 0; i < (DIMM_PID_HISTORY_SIZE - 1); i++) {
-			state->cur_sample = (state->cur_sample + 1) %
-				DIMM_PID_HISTORY_SIZE;
-			state->sample_history[state->cur_sample] = temp;
-			state->error_history[state->cur_sample] =
-				temp - DIMM_PID_INPUT_TARGET;
-		}
-		state->first = 0;
-	}
-
-	/* Calculate the integral term */
-	sum = 0;
-	integral = 0;
-	for (i = 0; i < DIMM_PID_HISTORY_SIZE; i++)
-		integral += state->error_history[i];
-	integral *= DIMM_PID_INTERVAL;
-	DBG("  integral: %08x\n", integral);
-	integ_p = ((s64)DIMM_PID_G_r) * (s64)integral;
-	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
-	sum += integ_p;
-
-	/* Calculate the derivative term */
-	derivative = state->error_history[state->cur_sample] -
-		state->error_history[(state->cur_sample + DIMM_PID_HISTORY_SIZE - 1)
-				    % DIMM_PID_HISTORY_SIZE];
-	derivative /= DIMM_PID_INTERVAL;
-	deriv_p = ((s64)DIMM_PID_G_d) * (s64)derivative;
-	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
-	sum += deriv_p;
-
-	/* Calculate the proportional term */
-	prop_p = ((s64)DIMM_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
-	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
-	sum += prop_p;
-
-	/* Scale sum */
-	sum >>= 36;
-
-	DBG("   sum: %d\n", (int)sum);
-	state->output = (s32)sum;
-	state->output = max(state->output, DIMM_PID_OUTPUT_MIN);
-	state->output = min(state->output, DIMM_PID_OUTPUT_MAX);
-	dimm_output_clamp = state->output;
-
-	DBG("** DIMM clamp value: %d\n", (int)state->output);
-
-	/* Backside PID is only every 5 seconds, force backside fan clamping now */
-	fan_min = (dimm_output_clamp * 100) / 14000;
-	fan_min = max(fan_min, backside_params.output_min);
-	if (backside_state.pwm < fan_min) {
-		backside_state.pwm = fan_min;
-		DBG(" -> applying clamp to backside fan now: %d  !\n", fan_min);
-		set_pwm_fan(BACKSIDE_FAN_PWM_INDEX, fan_min);
-	}
-}
-
-/*
- * Initialize the state structure for the DIMM temp control loop
- */
-static int init_dimms_state(struct dimm_pid_state *state)
-{
-	state->ticks = 1;
-	state->first = 1;
-	state->output = 4000;
-
-	state->monitor = attach_i2c_chip(XSERVE_DIMMS_LM87, "dimms_temp");
-	if (state->monitor == NULL)
-		return -ENODEV;
-
-	if (device_create_file(&of_dev->dev, &dev_attr_dimms_temperature))
-		printk(KERN_WARNING "Failed to create attribute file"
-			" for DIMM temperature\n");
-
-	return 0;
-}
-
-/*
- * Dispose of the state data for the DIMM control loop
- */
-static void dispose_dimms_state(struct dimm_pid_state *state)
-{
-	if (state->monitor == NULL)
-		return;
-
-	device_remove_file(&of_dev->dev, &dev_attr_dimms_temperature);
-
-	state->monitor = NULL;
-}
-
-/*
- * Slots fan control loop
- */
-static void do_monitor_slots(struct slots_pid_state *state)
-{
-	s32 temp, integral, derivative;
-	s64 integ_p, deriv_p, prop_p, sum;
-	int i, rc;
-
-	if (--state->ticks != 0)
-		return;
-	state->ticks = SLOTS_PID_INTERVAL;
-
-	DBG("slots:\n");
-
-	/* Check fan status */
-	rc = get_pwm_fan(SLOTS_FAN_PWM_INDEX);
-	if (rc < 0) {
-		printk(KERN_WARNING "Error %d reading slots fan !\n", rc);
-		/* XXX What do we do now ? */
-	} else
-		state->pwm = rc;
-	DBG("  current pwm: %d\n", state->pwm);
-
-	/* Get some sensor readings */
-	temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor,
-						    DS1775_TEMP)) << 8;
-	state->last_temp = temp;
-	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
-	    FIX32TOPRINT(SLOTS_PID_INPUT_TARGET));
-
-	/* Store temperature and error in history array */
-	state->cur_sample = (state->cur_sample + 1) % SLOTS_PID_HISTORY_SIZE;
-	state->sample_history[state->cur_sample] = temp;
-	state->error_history[state->cur_sample] = temp - SLOTS_PID_INPUT_TARGET;
-
-	/* If first loop, fill the history table */
-	if (state->first) {
-		for (i = 0; i < (SLOTS_PID_HISTORY_SIZE - 1); i++) {
-			state->cur_sample = (state->cur_sample + 1) %
-				SLOTS_PID_HISTORY_SIZE;
-			state->sample_history[state->cur_sample] = temp;
-			state->error_history[state->cur_sample] =
-				temp - SLOTS_PID_INPUT_TARGET;
-		}
-		state->first = 0;
-	}
-
-	/* Calculate the integral term */
-	sum = 0;
-	integral = 0;
-	for (i = 0; i < SLOTS_PID_HISTORY_SIZE; i++)
-		integral += state->error_history[i];
-	integral *= SLOTS_PID_INTERVAL;
-	DBG("  integral: %08x\n", integral);
-	integ_p = ((s64)SLOTS_PID_G_r) * (s64)integral;
-	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
-	sum += integ_p;
-
-	/* Calculate the derivative term */
-	derivative = state->error_history[state->cur_sample] -
-		state->error_history[(state->cur_sample + SLOTS_PID_HISTORY_SIZE - 1)
-				    % SLOTS_PID_HISTORY_SIZE];
-	derivative /= SLOTS_PID_INTERVAL;
-	deriv_p = ((s64)SLOTS_PID_G_d) * (s64)derivative;
-	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
-	sum += deriv_p;
-
-	/* Calculate the proportional term */
-	prop_p = ((s64)SLOTS_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
-	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
-	sum += prop_p;
-
-	/* Scale sum */
-	sum >>= 36;
-
-	DBG("   sum: %d\n", (int)sum);
-	state->pwm = (s32)sum;
-
-	state->pwm = max(state->pwm, SLOTS_PID_OUTPUT_MIN);
-	state->pwm = min(state->pwm, SLOTS_PID_OUTPUT_MAX);
-
-	DBG("** DRIVES PWM: %d\n", (int)state->pwm);
-	set_pwm_fan(SLOTS_FAN_PWM_INDEX, state->pwm);
-}
-
-/*
- * Initialize the state structure for the slots bay fan control loop
- */
-static int init_slots_state(struct slots_pid_state *state)
-{
-	int err;
-
-	state->ticks = 1;
-	state->first = 1;
-	state->pwm = 50;
-
-	state->monitor = attach_i2c_chip(XSERVE_SLOTS_LM75, "slots_temp");
-	if (state->monitor == NULL)
-		return -ENODEV;
-
-	err = device_create_file(&of_dev->dev, &dev_attr_slots_temperature);
-	err |= device_create_file(&of_dev->dev, &dev_attr_slots_fan_pwm);
-	if (err)
-		printk(KERN_WARNING "Failed to create attribute file(s)"
-			" for slots bay fan\n");
-
-	return 0;
-}
-
-/*
- * Dispose of the state data for the slots control loop
- */
-static void dispose_slots_state(struct slots_pid_state *state)
-{
-	if (state->monitor == NULL)
-		return;
-
-	device_remove_file(&of_dev->dev, &dev_attr_slots_temperature);
-	device_remove_file(&of_dev->dev, &dev_attr_slots_fan_pwm);
-
-	state->monitor = NULL;
-}
-
-
-static int call_critical_overtemp(void)
-{
-	char *argv[] = { critical_overtemp_path, NULL };
-	static char *envp[] = { "HOME=/",
-				"TERM=linux",
-				"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
-				NULL };
-
-	return call_usermodehelper(critical_overtemp_path,
-				   argv, envp, UMH_WAIT_EXEC);
-}
-
-
-/*
- * Here's the kernel thread that calls the various control loops
- */
-static int main_control_loop(void *x)
-{
-	DBG("main_control_loop started\n");
-
-	mutex_lock(&driver_lock);
-
-	if (start_fcu() < 0) {
-		printk(KERN_ERR "kfand: failed to start FCU\n");
-		mutex_unlock(&driver_lock);
-		goto out;
-	}
-
-	/* Set the PCI fan once for now on non-RackMac */
-	if (!rackmac)
-		set_pwm_fan(SLOTS_FAN_PWM_INDEX, SLOTS_FAN_DEFAULT_PWM);
-
-	/* Initialize ADCs */
-	initialize_adc(&processor_state[0]);
-	if (processor_state[1].monitor != NULL)
-		initialize_adc(&processor_state[1]);
-
-	fcu_tickle_ticks = FCU_TICKLE_TICKS;
-
-	mutex_unlock(&driver_lock);
-
-	while (state == state_attached) {
-		unsigned long elapsed, start;
-
-		start = jiffies;
-
-		mutex_lock(&driver_lock);
-
-		/* Tickle the FCU just in case */
-		if (--fcu_tickle_ticks < 0) {
-			fcu_tickle_ticks = FCU_TICKLE_TICKS;
-			tickle_fcu();
-		}
-
-		/* First, we always calculate the new DIMMs state on an Xserve */
-		if (rackmac)
-			do_monitor_dimms(&dimms_state);
-
-		/* Then, the CPUs */
-		if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
-			do_monitor_cpu_combined();
-		else if (cpu_pid_type == CPU_PID_TYPE_RACKMAC) {
-			do_monitor_cpu_rack(&processor_state[0]);
-			if (processor_state[1].monitor != NULL)
-				do_monitor_cpu_rack(&processor_state[1]);
-			// better deal with UP
-		} else {
-			do_monitor_cpu_split(&processor_state[0]);
-			if (processor_state[1].monitor != NULL)
-				do_monitor_cpu_split(&processor_state[1]);
-			// better deal with UP
-		}
-		/* Then, the rest */
-		do_monitor_backside(&backside_state);
-		if (rackmac)
-			do_monitor_slots(&slots_state);
-		else
-			do_monitor_drives(&drives_state);
-		mutex_unlock(&driver_lock);
-
-		if (critical_state == 1) {
-			printk(KERN_WARNING "Temperature control detected a critical condition\n");
-			printk(KERN_WARNING "Attempting to shut down...\n");
-			if (call_critical_overtemp()) {
-				printk(KERN_WARNING "Can't call %s, power off now!\n",
-				       critical_overtemp_path);
-				machine_power_off();
-			}
-		}
-		if (critical_state > 0)
-			critical_state++;
-		if (critical_state > MAX_CRITICAL_STATE) {
-			printk(KERN_WARNING "Shutdown timed out, power off now !\n");
-			machine_power_off();
-		}
-
-		// FIXME: Deal with signals
-		elapsed = jiffies - start;
-		if (elapsed < HZ)
-			schedule_timeout_interruptible(HZ - elapsed);
-	}
-
- out:
-	DBG("main_control_loop ended\n");
-
-	ctrl_task = 0;
-	complete_and_exit(&ctrl_complete, 0);
-}
-
-/*
- * Dispose the control loops when tearing down
- */
-static void dispose_control_loops(void)
-{
-	dispose_processor_state(&processor_state[0]);
-	dispose_processor_state(&processor_state[1]);
-	dispose_backside_state(&backside_state);
-	dispose_drives_state(&drives_state);
-	dispose_slots_state(&slots_state);
-	dispose_dimms_state(&dimms_state);
-}
-
-/*
- * Create the control loops. U3-0 i2c bus is up, so we can now
- * get to the various sensors
- */
-static int create_control_loops(void)
-{
-	struct device_node *np;
-
-	/* Count CPUs from the device-tree, we don't care how many are
-	 * actually used by Linux
-	 */
-	cpu_count = 0;
-	for (np = NULL; NULL != (np = of_find_node_by_type(np, "cpu"));)
-		cpu_count++;
-
-	DBG("counted %d CPUs in the device-tree\n", cpu_count);
-
-	/* Decide the type of PID algorithm to use based on the presence of
-	 * the pumps, though that may not be the best way, that is good enough
-	 * for now
-	 */
-	if (rackmac)
-		cpu_pid_type = CPU_PID_TYPE_RACKMAC;
-	else if (of_machine_is_compatible("PowerMac7,3")
-	    && (cpu_count > 1)
-	    && fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID
-	    && fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) {
-		printk(KERN_INFO "Liquid cooling pumps detected, using new algorithm !\n");
-		cpu_pid_type = CPU_PID_TYPE_COMBINED;
-	} else
-		cpu_pid_type = CPU_PID_TYPE_SPLIT;
-
-	/* Create control loops for everything. If any fail, everything
-	 * fails
-	 */
-	if (init_processor_state(&processor_state[0], 0))
-		goto fail;
-	if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
-		fetch_cpu_pumps_minmax();
-
-	if (cpu_count > 1 && init_processor_state(&processor_state[1], 1))
-		goto fail;
-	if (init_backside_state(&backside_state))
-		goto fail;
-	if (rackmac && init_dimms_state(&dimms_state))
-		goto fail;
-	if (rackmac && init_slots_state(&slots_state))
-		goto fail;
-	if (!rackmac && init_drives_state(&drives_state))
-		goto fail;
-
-	DBG("all control loops up !\n");
-
-	return 0;
-	
- fail:
-	DBG("failure creating control loops, disposing\n");
-
-	dispose_control_loops();
-
-	return -ENODEV;
-}
-
-/*
- * Start the control loops after everything is up, that is create
- * the thread that will make them run
- */
-static void start_control_loops(void)
-{
-	init_completion(&ctrl_complete);
-
-	ctrl_task = kthread_run(main_control_loop, NULL, "kfand");
-}
-
-/*
- * Stop the control loops when tearing down
- */
-static void stop_control_loops(void)
-{
-	if (ctrl_task)
-		wait_for_completion(&ctrl_complete);
-}
-
-/*
- * Attach to the i2c FCU after detecting U3-1 bus
- */
-static int attach_fcu(void)
-{
-	fcu = attach_i2c_chip(FAN_CTRLER_ID, "fcu");
-	if (fcu == NULL)
-		return -ENODEV;
-
-	DBG("FCU attached\n");
-
-	return 0;
-}
-
-/*
- * Detach from the i2c FCU when tearing down
- */
-static void detach_fcu(void)
-{
-	fcu = NULL;
-}
-
-/*
- * Attach to the i2c controller. We probe the various chips based
- * on the device-tree nodes and build everything for the driver to
- * run, we then kick the driver monitoring thread
- */
-static int therm_pm72_attach(struct i2c_adapter *adapter)
-{
-	mutex_lock(&driver_lock);
-
-	/* Check state */
-	if (state == state_detached)
-		state = state_attaching;
-	if (state != state_attaching) {
-		mutex_unlock(&driver_lock);
-		return 0;
-	}
-
-	/* Check if we are looking for one of these */
-	if (u3_0 == NULL && !strcmp(adapter->name, "u3 0")) {
-		u3_0 = adapter;
-		DBG("found U3-0\n");
-		if (k2 || !rackmac)
-			if (create_control_loops())
-				u3_0 = NULL;
-	} else if (u3_1 == NULL && !strcmp(adapter->name, "u3 1")) {
-		u3_1 = adapter;
-		DBG("found U3-1, attaching FCU\n");
-		if (attach_fcu())
-			u3_1 = NULL;
-	} else if (k2 == NULL && !strcmp(adapter->name, "mac-io 0")) {
-		k2 = adapter;
-		DBG("Found K2\n");
-		if (u3_0 && rackmac)
-			if (create_control_loops())
-				k2 = NULL;
-	}
-	/* We got all we need, start control loops */
-	if (u3_0 != NULL && u3_1 != NULL && (k2 || !rackmac)) {
-		DBG("everything up, starting control loops\n");
-		state = state_attached;
-		start_control_loops();
-	}
-	mutex_unlock(&driver_lock);
-
-	return 0;
-}
-
-static int therm_pm72_probe(struct i2c_client *client,
-			    const struct i2c_device_id *id)
-{
-	/* Always succeed, the real work was done in therm_pm72_attach() */
-	return 0;
-}
-
-/*
- * Called when any of the devices which participates into thermal management
- * is going away.
- */
-static int therm_pm72_remove(struct i2c_client *client)
-{
-	struct i2c_adapter *adapter = client->adapter;
-
-	mutex_lock(&driver_lock);
-
-	if (state != state_detached)
-		state = state_detaching;
-
-	/* Stop control loops if any */
-	DBG("stopping control loops\n");
-	mutex_unlock(&driver_lock);
-	stop_control_loops();
-	mutex_lock(&driver_lock);
-
-	if (u3_0 != NULL && !strcmp(adapter->name, "u3 0")) {
-		DBG("lost U3-0, disposing control loops\n");
-		dispose_control_loops();
-		u3_0 = NULL;
-	}
-	
-	if (u3_1 != NULL && !strcmp(adapter->name, "u3 1")) {
-		DBG("lost U3-1, detaching FCU\n");
-		detach_fcu();
-		u3_1 = NULL;
-	}
-	if (u3_0 == NULL && u3_1 == NULL)
-		state = state_detached;
-
-	mutex_unlock(&driver_lock);
-
-	return 0;
-}
-
-/*
- * i2c_driver structure to attach to the host i2c controller
- */
-
-static const struct i2c_device_id therm_pm72_id[] = {
-	/*
-	 * Fake device name, thermal management is done by several
-	 * chips but we don't need to differentiate between them at
-	 * this point.
-	 */
-	{ "therm_pm72", 0 },
-	{ }
-};
-
-static struct i2c_driver therm_pm72_driver = {
-	.driver = {
-		.name	= "therm_pm72",
-	},
-	.attach_adapter	= therm_pm72_attach,
-	.probe		= therm_pm72_probe,
-	.remove		= therm_pm72_remove,
-	.id_table	= therm_pm72_id,
-};
-
-static int fan_check_loc_match(const char *loc, int fan)
-{
-	char	tmp[64];
-	char	*c, *e;
-
-	strlcpy(tmp, fcu_fans[fan].loc, 64);
-
-	c = tmp;
-	for (;;) {
-		e = strchr(c, ',');
-		if (e)
-			*e = 0;
-		if (strcmp(loc, c) == 0)
-			return 1;
-		if (e == NULL)
-			break;
-		c = e + 1;
-	}
-	return 0;
-}
-
-static void fcu_lookup_fans(struct device_node *fcu_node)
-{
-	struct device_node *np = NULL;
-	int i;
-
-	/* The table is filled by default with values that are suitable
-	 * for the old machines without device-tree informations. We scan
-	 * the device-tree and override those values with whatever is
-	 * there
-	 */
-
-	DBG("Looking up FCU controls in device-tree...\n");
-
-	while ((np = of_get_next_child(fcu_node, np)) != NULL) {
-		int type = -1;
-		const char *loc;
-		const u32 *reg;
-
-		DBG(" control: %s, type: %s\n", np->name, np->type);
-
-		/* Detect control type */
-		if (!strcmp(np->type, "fan-rpm-control") ||
-		    !strcmp(np->type, "fan-rpm"))
-			type = FCU_FAN_RPM;
-		if (!strcmp(np->type, "fan-pwm-control") ||
-		    !strcmp(np->type, "fan-pwm"))
-			type = FCU_FAN_PWM;
-		/* Only care about fans for now */
-		if (type == -1)
-			continue;
-
-		/* Lookup for a matching location */
-		loc = of_get_property(np, "location", NULL);
-		reg = of_get_property(np, "reg", NULL);
-		if (loc == NULL || reg == NULL)
-			continue;
-		DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg);
-
-		for (i = 0; i < FCU_FAN_COUNT; i++) {
-			int fan_id;
-
-			if (!fan_check_loc_match(loc, i))
-				continue;
-			DBG(" location match, index: %d\n", i);
-			fcu_fans[i].id = FCU_FAN_ABSENT_ID;
-			if (type != fcu_fans[i].type) {
-				printk(KERN_WARNING "therm_pm72: Fan type mismatch "
-				       "in device-tree for %s\n", np->full_name);
-				break;
-			}
-			if (type == FCU_FAN_RPM)
-				fan_id = ((*reg) - 0x10) / 2;
-			else
-				fan_id = ((*reg) - 0x30) / 2;
-			if (fan_id > 7) {
-				printk(KERN_WARNING "therm_pm72: Can't parse "
-				       "fan ID in device-tree for %s\n", np->full_name);
-				break;
-			}
-			DBG(" fan id -> %d, type -> %d\n", fan_id, type);
-			fcu_fans[i].id = fan_id;
-		}
-	}
-
-	/* Now dump the array */
-	printk(KERN_INFO "Detected fan controls:\n");
-	for (i = 0; i < FCU_FAN_COUNT; i++) {
-		if (fcu_fans[i].id == FCU_FAN_ABSENT_ID)
-			continue;
-		printk(KERN_INFO "  %d: %s fan, id %d, location: %s\n", i,
-		       fcu_fans[i].type == FCU_FAN_RPM ? "RPM" : "PWM",
-		       fcu_fans[i].id, fcu_fans[i].loc);
-	}
-}
-
-static int fcu_of_probe(struct platform_device* dev)
-{
-	state = state_detached;
-	of_dev = dev;
-
-	dev_info(&dev->dev, "PowerMac G5 Thermal control driver %s\n", VERSION);
-
-	/* Lookup the fans in the device tree */
-	fcu_lookup_fans(dev->dev.of_node);
-
-	/* Add the driver */
-	return i2c_add_driver(&therm_pm72_driver);
-}
-
-static int fcu_of_remove(struct platform_device* dev)
-{
-	i2c_del_driver(&therm_pm72_driver);
-
-	return 0;
-}
-
-static const struct of_device_id fcu_match[] = 
-{
-	{
-	.type		= "fcu",
-	},
-	{},
-};
-MODULE_DEVICE_TABLE(of, fcu_match);
-
-static struct platform_driver fcu_of_platform_driver = 
-{
-	.driver = {
-		.name = "temperature",
-		.owner = THIS_MODULE,
-		.of_match_table = fcu_match,
-	},
-	.probe		= fcu_of_probe,
-	.remove		= fcu_of_remove
-};
-
-/*
- * Check machine type, attach to i2c controller
- */
-static int __init therm_pm72_init(void)
-{
-	rackmac = of_machine_is_compatible("RackMac3,1");
-
-	if (!of_machine_is_compatible("PowerMac7,2") &&
-	    !of_machine_is_compatible("PowerMac7,3") &&
-	    !rackmac)
-	    	return -ENODEV;
-
-	return platform_driver_register(&fcu_of_platform_driver);
-}
-
-static void __exit therm_pm72_exit(void)
-{
-	platform_driver_unregister(&fcu_of_platform_driver);
-}
-
-module_init(therm_pm72_init);
-module_exit(therm_pm72_exit);
-
-MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
-MODULE_DESCRIPTION("Driver for Apple's PowerMac G5 thermal control");
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/macintosh/therm_pm72.h b/drivers/macintosh/therm_pm72.h
deleted file mode 100644
index df3680e2a22f..000000000000
--- a/drivers/macintosh/therm_pm72.h
+++ /dev/null
@@ -1,326 +0,0 @@
-#ifndef __THERM_PMAC_7_2_H__
-#define __THERM_PMAC_7_2_H__
-
-typedef unsigned short fu16;
-typedef int fs32;
-typedef short fs16;
-
-struct mpu_data
-{
-	u8	signature;		/* 0x00 - EEPROM sig. */
-	u8	bytes_used;		/* 0x01 - Bytes used in eeprom (160 ?) */
-	u8	size;			/* 0x02 - EEPROM size (256 ?) */
-	u8	version;		/* 0x03 - EEPROM version */
-	u32	data_revision;		/* 0x04 - Dataset revision */
-	u8	processor_bin_code[3];	/* 0x08 - Processor BIN code */
-	u8	bin_code_expansion;	/* 0x0b - ??? (padding ?) */
-	u8	processor_num;		/* 0x0c - Number of CPUs on this MPU */
-	u8	input_mul_bus_div;	/* 0x0d - Clock input multiplier/bus divider */
-	u8	reserved1[2];		/* 0x0e - */
-	u32	input_clk_freq_high;	/* 0x10 - Input clock frequency high */
-	u8	cpu_nb_target_cycles;	/* 0x14 - ??? */
-	u8	cpu_statlat;		/* 0x15 - ??? */
-	u8	cpu_snooplat;		/* 0x16 - ??? */
-	u8	cpu_snoopacc;		/* 0x17 - ??? */
-	u8	nb_paamwin;		/* 0x18 - ??? */
-	u8	nb_statlat;		/* 0x19 - ??? */
-	u8	nb_snooplat;		/* 0x1a - ??? */
-	u8	nb_snoopwin;		/* 0x1b - ??? */
-	u8	api_bus_mode;		/* 0x1c - ??? */
-	u8	reserved2[3];		/* 0x1d - */
-	u32	input_clk_freq_low;	/* 0x20 - Input clock frequency low */
-	u8	processor_card_slot;	/* 0x24 - Processor card slot number */
-	u8	reserved3[2];		/* 0x25 - */
-	u8	padjmax;       		/* 0x27 - Max power adjustment (Not in OF!) */
-	u8	ttarget;		/* 0x28 - Target temperature */
-	u8	tmax;			/* 0x29 - Max temperature */
-	u8	pmaxh;			/* 0x2a - Max power */
-	u8	tguardband;		/* 0x2b - Guardband temp ??? Hist. len in OSX */
-	fs32	pid_gp;			/* 0x2c - PID proportional gain */
-	fs32	pid_gr;			/* 0x30 - PID reset gain */
-	fs32	pid_gd;			/* 0x34 - PID derivative gain */
-	fu16	voph;			/* 0x38 - Vop High */
-	fu16	vopl;			/* 0x3a - Vop Low */
-	fs16	nactual_die;		/* 0x3c - nActual Die */
-	fs16	nactual_heatsink;	/* 0x3e - nActual Heatsink */
-	fs16	nactual_system;		/* 0x40 - nActual System */
-	u16	calibration_flags;	/* 0x42 - Calibration flags */
-	fu16	mdiode;			/* 0x44 - Diode M value (scaling factor) */
-	fs16	bdiode;			/* 0x46 - Diode B value (offset) */
-	fs32	theta_heat_sink;	/* 0x48 - Theta heat sink */
-	u16	rminn_intake_fan;	/* 0x4c - Intake fan min RPM */
-	u16	rmaxn_intake_fan;	/* 0x4e - Intake fan max RPM */
-	u16	rminn_exhaust_fan;	/* 0x50 - Exhaust fan min RPM */
-	u16	rmaxn_exhaust_fan;	/* 0x52 - Exhaust fan max RPM */
-	u8	processor_part_num[8];	/* 0x54 - Processor part number XX pumps min/max */
-	u32	processor_lot_num;	/* 0x5c - Processor lot number */
-	u8	orig_card_sernum[0x10];	/* 0x60 - Card original serial number */
-	u8	curr_card_sernum[0x10];	/* 0x70 - Card current serial number */
-	u8	mlb_sernum[0x18];	/* 0x80 - MLB serial number */
-	u32	checksum1;		/* 0x98 - */
-	u32	checksum2;		/* 0x9c - */	
-}; /* Total size = 0xa0 */
-
-/* Display a 16.16 fixed point value */
-#define FIX32TOPRINT(f)	((f) >> 16),((((f) & 0xffff) * 1000) >> 16)
-
-/*
- * Maximum number of seconds to be in critical state (after a
- * normal shutdown attempt). If the machine isn't down after
- * this counter elapses, we force an immediate machine power
- * off.
- */
-#define MAX_CRITICAL_STATE			30
-static char * critical_overtemp_path = "/sbin/critical_overtemp";
-
-/*
- * This option is "weird" :) Basically, if you define this to 1
- * the control loop for the RPMs fans (not PWMs) will apply the
- * correction factor obtained from the PID to the _actual_ RPM
- * speed read from the FCU.
- * If you define the below constant to 0, then it will be
- * applied to the setpoint RPM speed, that is basically the
- * speed we proviously "asked" for.
- *
- * I'm not sure which of these Apple's algorithm is supposed
- * to use
- */
-#define RPM_PID_USE_ACTUAL_SPEED		0
-
-/*
- * i2c IDs. Currently, we hard code those and assume that
- * the FCU is on U3 bus 1 while all sensors are on U3 bus
- * 0. This appear to be safe enough for this first version
- * of the driver, though I would accept any clean patch
- * doing a better use of the device-tree without turning the
- * while i2c registration mechanism into a racy mess
- *
- * Note: Xserve changed this. We have some bits on the K2 bus,
- * which I arbitrarily set to 0x200. Ultimately, we really want
- * too lookup these in the device-tree though
- */
-#define FAN_CTRLER_ID		0x15e
-#define SUPPLY_MONITOR_ID      	0x58
-#define SUPPLY_MONITORB_ID     	0x5a
-#define DRIVES_DALLAS_ID	0x94
-#define BACKSIDE_MAX_ID		0x98
-#define XSERVE_DIMMS_LM87	0x25a
-#define XSERVE_SLOTS_LM75	0x290
-
-/*
- * Some MAX6690, DS1775, LM87 register definitions
- */
-#define MAX6690_INT_TEMP	0
-#define MAX6690_EXT_TEMP	1
-#define DS1775_TEMP		0
-#define LM87_INT_TEMP		0x27
-
-/*
- * Scaling factors for the AD7417 ADC converters (except
- * for the CPU diode which is obtained from the EEPROM).
- * Those values are obtained from the property list of
- * the darwin driver
- */
-#define ADC_12V_CURRENT_SCALE	0x0320	/* _AD2 */
-#define ADC_CPU_VOLTAGE_SCALE	0x00a0	/* _AD3 */
-#define ADC_CPU_CURRENT_SCALE	0x1f40	/* _AD4 */
-
-/*
- * PID factors for the U3/Backside fan control loop. We have 2 sets
- * of values here, one set for U3 and one set for U3H
- */
-#define BACKSIDE_FAN_PWM_DEFAULT_ID	1
-#define BACKSIDE_FAN_PWM_INDEX		0
-#define BACKSIDE_PID_U3_G_d		0x02800000
-#define BACKSIDE_PID_U3H_G_d		0x01400000
-#define BACKSIDE_PID_RACK_G_d		0x00500000
-#define BACKSIDE_PID_G_p		0x00500000
-#define BACKSIDE_PID_RACK_G_p		0x0004cccc
-#define BACKSIDE_PID_G_r		0x00000000
-#define BACKSIDE_PID_U3_INPUT_TARGET	0x00410000
-#define BACKSIDE_PID_U3H_INPUT_TARGET	0x004b0000
-#define BACKSIDE_PID_RACK_INPUT_TARGET	0x00460000
-#define BACKSIDE_PID_INTERVAL		5
-#define BACKSIDE_PID_RACK_INTERVAL	1
-#define BACKSIDE_PID_OUTPUT_MAX		100
-#define BACKSIDE_PID_U3_OUTPUT_MIN	20
-#define BACKSIDE_PID_U3H_OUTPUT_MIN	20
-#define BACKSIDE_PID_HISTORY_SIZE	2
-
-struct basckside_pid_params
-{
-	s32			G_d;
-	s32			G_p;
-	s32			G_r;
-	s32			input_target;
-	s32			output_min;
-	s32			output_max;
-	s32			interval;
-	int			additive;
-};
-
-struct backside_pid_state
-{
-	int			ticks;
-	struct i2c_client *	monitor;
-	s32		       	sample_history[BACKSIDE_PID_HISTORY_SIZE];
-	s32			error_history[BACKSIDE_PID_HISTORY_SIZE];
-	int			cur_sample;
-	s32			last_temp;
-	int			pwm;
-	int			first;
-};
-
-/*
- * PID factors for the Drive Bay fan control loop
- */
-#define DRIVES_FAN_RPM_DEFAULT_ID	2
-#define DRIVES_FAN_RPM_INDEX		1
-#define DRIVES_PID_G_d			0x01e00000
-#define DRIVES_PID_G_p			0x00500000
-#define DRIVES_PID_G_r			0x00000000
-#define DRIVES_PID_INPUT_TARGET		0x00280000
-#define DRIVES_PID_INTERVAL    		5
-#define DRIVES_PID_OUTPUT_MAX		4000
-#define DRIVES_PID_OUTPUT_MIN		300
-#define DRIVES_PID_HISTORY_SIZE		2
-
-struct drives_pid_state
-{
-	int			ticks;
-	struct i2c_client *	monitor;
-	s32	       		sample_history[BACKSIDE_PID_HISTORY_SIZE];
-	s32			error_history[BACKSIDE_PID_HISTORY_SIZE];
-	int			cur_sample;
-	s32			last_temp;
-	int			rpm;
-	int			first;
-};
-
-#define SLOTS_FAN_PWM_DEFAULT_ID	2
-#define SLOTS_FAN_PWM_INDEX		2
-#define	SLOTS_FAN_DEFAULT_PWM		40 /* Do better here ! */
-
-
-/*
- * PID factors for the Xserve DIMM control loop
- */
-#define DIMM_PID_G_d			0
-#define DIMM_PID_G_p			0
-#define DIMM_PID_G_r			0x06553600
-#define DIMM_PID_INPUT_TARGET		3276800
-#define DIMM_PID_INTERVAL    		1
-#define DIMM_PID_OUTPUT_MAX		14000
-#define DIMM_PID_OUTPUT_MIN		4000
-#define DIMM_PID_HISTORY_SIZE		20
-
-struct dimm_pid_state
-{
-	int			ticks;
-	struct i2c_client *	monitor;
-	s32	       		sample_history[DIMM_PID_HISTORY_SIZE];
-	s32			error_history[DIMM_PID_HISTORY_SIZE];
-	int			cur_sample;
-	s32			last_temp;
-	int			first;
-	int			output;
-};
-
-
-/*
- * PID factors for the Xserve Slots control loop
- */
-#define SLOTS_PID_G_d			0
-#define SLOTS_PID_G_p			0
-#define SLOTS_PID_G_r			0x00100000
-#define SLOTS_PID_INPUT_TARGET		3200000
-#define SLOTS_PID_INTERVAL    		1
-#define SLOTS_PID_OUTPUT_MAX		100
-#define SLOTS_PID_OUTPUT_MIN		20
-#define SLOTS_PID_HISTORY_SIZE		20
-
-struct slots_pid_state
-{
-	int			ticks;
-	struct i2c_client *	monitor;
-	s32	       		sample_history[SLOTS_PID_HISTORY_SIZE];
-	s32			error_history[SLOTS_PID_HISTORY_SIZE];
-	int			cur_sample;
-	s32			last_temp;
-	int			first;
-	int			pwm;
-};
-
-
-
-/* Desktops */
-
-#define CPUA_INTAKE_FAN_RPM_DEFAULT_ID	3
-#define CPUA_EXHAUST_FAN_RPM_DEFAULT_ID	4
-#define CPUB_INTAKE_FAN_RPM_DEFAULT_ID	5
-#define CPUB_EXHAUST_FAN_RPM_DEFAULT_ID	6
-
-#define CPUA_INTAKE_FAN_RPM_INDEX	3
-#define CPUA_EXHAUST_FAN_RPM_INDEX	4
-#define CPUB_INTAKE_FAN_RPM_INDEX	5
-#define CPUB_EXHAUST_FAN_RPM_INDEX	6
-
-#define CPU_INTAKE_SCALE		0x0000f852
-#define CPU_TEMP_HISTORY_SIZE		2
-#define CPU_POWER_HISTORY_SIZE		10
-#define CPU_PID_INTERVAL		1
-#define CPU_MAX_OVERTEMP		90
-
-#define CPUA_PUMP_RPM_INDEX		7
-#define CPUB_PUMP_RPM_INDEX		8
-#define CPU_PUMP_OUTPUT_MAX		3200
-#define CPU_PUMP_OUTPUT_MIN		1250
-
-/* Xserve */
-#define CPU_A1_FAN_RPM_INDEX		9
-#define CPU_A2_FAN_RPM_INDEX		10
-#define CPU_A3_FAN_RPM_INDEX		11
-#define CPU_B1_FAN_RPM_INDEX		12
-#define CPU_B2_FAN_RPM_INDEX		13
-#define CPU_B3_FAN_RPM_INDEX		14
-
-
-struct cpu_pid_state
-{
-	int			index;
-	struct i2c_client *	monitor;
-	struct mpu_data		mpu;
-	int			overtemp;
-	s32	       		temp_history[CPU_TEMP_HISTORY_SIZE];
-	int			cur_temp;
-	s32			power_history[CPU_POWER_HISTORY_SIZE];
-	s32			error_history[CPU_POWER_HISTORY_SIZE];
-	int			cur_power;
-	int			count_power;
-	int			rpm;
-	int			intake_rpm;
-	s32			voltage;
-	s32			current_a;
-	s32			last_temp;
-	s32			last_power;
-	int			first;
-	u8			adc_config;
-	s32			pump_min;
-	s32			pump_max;
-};
-
-/* Tickle FCU every 10 seconds */
-#define FCU_TICKLE_TICKS	10
-
-/*
- * Driver state
- */
-enum {
-	state_detached,
-	state_attaching,
-	state_attached,
-	state_detaching,
-};
-
-
-#endif /* __THERM_PMAC_7_2_H__ */
-- 
2.1.1

^ permalink raw reply related

* Re: [PATCH] macintosh: therm_pm72: delete deprecated driver
From: Benjamin Herrenschmidt @ 2014-11-06  2:19 UTC (permalink / raw)
  To: Wolfram Sang; +Cc: linuxppc-dev
In-Reply-To: <1415238358-2453-1-git-send-email-wsa@the-dreams.de>

On Thu, 2014-11-06 at 02:45 +0100, Wolfram Sang wrote:
> The new driver is around for more than 2 years now, so the old one can
> go. Getting rid of it helps the removal of the legacy .attach_adapter
> callback of the I2C subsystem.
> 
> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>

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

> ---
>  drivers/macintosh/Kconfig      |   10 -
>  drivers/macintosh/Makefile     |    1 -
>  drivers/macintosh/therm_pm72.c | 2279 ----------------------------------------
>  drivers/macintosh/therm_pm72.h |  326 ------
>  4 files changed, 2616 deletions(-)
>  delete mode 100644 drivers/macintosh/therm_pm72.c
>  delete mode 100644 drivers/macintosh/therm_pm72.h
> 
> diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
> index 3067d56b11a6..5844b80bd90e 100644
> --- a/drivers/macintosh/Kconfig
> +++ b/drivers/macintosh/Kconfig
> @@ -204,16 +204,6 @@ config THERM_ADT746X
>            iBook G4, and the ATI based aluminium PowerBooks, allowing slightly
>  	  better fan behaviour by default, and some manual control.
>  
> -config THERM_PM72
> -	tristate "Support for thermal management on PowerMac G5 (AGP)"
> -	depends on I2C && I2C_POWERMAC && PPC_PMAC64
> -	default n
> -	help
> -	  This driver provides thermostat and fan control for the desktop
> -	  G5 machines.
> -
> -	  This is deprecated, use windfarm instead.
> -
>  config WINDFARM
>  	tristate "New PowerMac thermal control infrastructure"
>  	depends on PPC
> diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
> index d2f0120bc878..383ba920085b 100644
> --- a/drivers/macintosh/Makefile
> +++ b/drivers/macintosh/Makefile
> @@ -25,7 +25,6 @@ obj-$(CONFIG_ADB_IOP)		+= adb-iop.o
>  obj-$(CONFIG_ADB_PMU68K)	+= via-pmu68k.o
>  obj-$(CONFIG_ADB_MACIO)		+= macio-adb.o
>  
> -obj-$(CONFIG_THERM_PM72)	+= therm_pm72.o
>  obj-$(CONFIG_THERM_WINDTUNNEL)	+= therm_windtunnel.o
>  obj-$(CONFIG_THERM_ADT746X)	+= therm_adt746x.o
>  obj-$(CONFIG_WINDFARM)	        += windfarm_core.o
> diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
> deleted file mode 100644
> index 97cfc5ac9fd0..000000000000
> --- a/drivers/macintosh/therm_pm72.c
> +++ /dev/null
> @@ -1,2279 +0,0 @@
> -/*
> - * Device driver for the thermostats & fan controller of  the
> - * Apple G5 "PowerMac7,2" desktop machines.
> - *
> - * (c) Copyright IBM Corp. 2003-2004
> - *
> - * Maintained by: Benjamin Herrenschmidt
> - *                <benh@kernel.crashing.org>
> - * 
> - *
> - * 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 7.0 snapshot property lists.
> - *
> - * As far as the CPUs control loops are concerned, I use the
> - * calibration & PID constants provided by the EEPROM,
> - * I do _not_ embed any value from the property lists, as the ones
> - * provided by Darwin 7.0 seem to always have an older version that
> - * what I've seen on the actual computers.
> - * It would be interesting to verify that though. Darwin has a
> - * version code of 1.0.0d11 for all control loops it seems, while
> - * so far, the machines EEPROMs contain a dataset versioned 1.0.0f
> - *
> - * Darwin doesn't provide source to all parts, some missing
> - * bits like the AppleFCU driver or the actual scale of some
> - * of the values returned by sensors had to be "guessed" some
> - * way... or based on what Open Firmware does.
> - *
> - * I didn't yet figure out how to get the slots power consumption
> - * out of the FCU, so that part has not been implemented yet and
> - * the slots fan is set to a fixed 50% PWM, hoping this value is
> - * safe enough ...
> - *
> - * Note: I have observed strange oscillations of the CPU control
> - * loop on a dual G5 here. When idle, the CPU exhaust fan tend to
> - * oscillates slowly (over several minutes) between the minimum
> - * of 300RPMs and approx. 1000 RPMs. I don't know what is causing
> - * this, it could be some incorrect constant or an error in the
> - * way I ported the algorithm, or it could be just normal. I
> - * don't have full understanding on the way Apple tweaked the PID
> - * algorithm for the CPU control, it is definitely not a standard
> - * implementation...
> - *
> - * TODO:  - Check MPU structure version/signature
> - *        - Add things like /sbin/overtemp for non-critical
> - *          overtemp conditions so userland can take some policy
> - *          decisions, like slowing down CPUs
> - *	  - Deal with fan and i2c failures in a better way
> - *	  - Maybe do a generic PID based on params used for
> - *	    U3 and Drives ? Definitely need to factor code a bit
> - *          better... also make sensor detection more robust using
> - *          the device-tree to probe for them
> - *        - Figure out how to get the slots consumption and set the
> - *          slots fan accordingly
> - *
> - * History:
> - *
> - *  Nov. 13, 2003 : 0.5
> - *	- First release
> - *
> - *  Nov. 14, 2003 : 0.6
> - *	- Read fan speed from FCU, low level fan routines now deal
> - *	  with errors & check fan status, though higher level don't
> - *	  do much.
> - *	- Move a bunch of definitions to .h file
> - *
> - *  Nov. 18, 2003 : 0.7
> - *	- Fix build on ppc64 kernel
> - *	- Move back statics definitions to .c file
> - *	- Avoid calling schedule_timeout with a negative number
> - *
> - *  Dec. 18, 2003 : 0.8
> - *	- Fix typo when reading back fan speed on 2 CPU machines
> - *
> - *  Mar. 11, 2004 : 0.9
> - *	- Rework code accessing the ADC chips, make it more robust and
> - *	  closer to the chip spec. Also make sure it is configured properly,
> - *        I've seen yet unexplained cases where on startup, I would have stale
> - *        values in the configuration register
> - *	- Switch back to use of target fan speed for PID, thus lowering
> - *        pressure on i2c
> - *
> - *  Oct. 20, 2004 : 1.1
> - *	- Add device-tree lookup for fan IDs, should detect liquid cooling
> - *        pumps when present
> - *	- Enable driver for PowerMac7,3 machines
> - *	- Split the U3/Backside cooling on U3 & U3H versions as Darwin does
> - *	- Add new CPU cooling algorithm for machines with liquid cooling
> - *	- Workaround for some PowerMac7,3 with empty "fan" node in the devtree
> - *	- Fix a signed/unsigned compare issue in some PID loops
> - *
> - *  Mar. 10, 2005 : 1.2
> - *	- Add basic support for Xserve G5
> - *	- Retrieve pumps min/max from EEPROM image in device-tree (broken)
> - *	- Use min/max macros here or there
> - *	- Latest darwin updated U3H min fan speed to 20% PWM
> - *
> - *  July. 06, 2006 : 1.3
> - *	- Fix setting of RPM fans on Xserve G5 (they were going too fast)
> - *      - Add missing slots fan control loop for Xserve G5
> - *	- Lower fixed slots fan speed from 50% to 40% on desktop G5s. We
> - *        still can't properly implement the control loop for these, so let's
> - *        reduce the noise a little bit, it appears that 40% still gives us
> - *        a pretty good air flow
> - *	- Add code to "tickle" the FCU regulary so it doesn't think that
> - *        we are gone while in fact, the machine just didn't need any fan
> - *        speed change lately
> - *
> - */
> -
> -#include <linux/types.h>
> -#include <linux/module.h>
> -#include <linux/errno.h>
> -#include <linux/kernel.h>
> -#include <linux/delay.h>
> -#include <linux/sched.h>
> -#include <linux/init.h>
> -#include <linux/spinlock.h>
> -#include <linux/wait.h>
> -#include <linux/reboot.h>
> -#include <linux/kmod.h>
> -#include <linux/i2c.h>
> -#include <linux/kthread.h>
> -#include <linux/mutex.h>
> -#include <linux/of_device.h>
> -#include <linux/of_platform.h>
> -#include <asm/prom.h>
> -#include <asm/machdep.h>
> -#include <asm/io.h>
> -#include <asm/sections.h>
> -#include <asm/macio.h>
> -
> -#include "therm_pm72.h"
> -
> -#define VERSION "1.3"
> -
> -#undef DEBUG
> -
> -#ifdef DEBUG
> -#define DBG(args...)	printk(args)
> -#else
> -#define DBG(args...)	do { } while(0)
> -#endif
> -
> -
> -/*
> - * Driver statics
> - */
> -
> -static struct platform_device *		of_dev;
> -static struct i2c_adapter *		u3_0;
> -static struct i2c_adapter *		u3_1;
> -static struct i2c_adapter *		k2;
> -static struct i2c_client *		fcu;
> -static struct cpu_pid_state		processor_state[2];
> -static struct basckside_pid_params	backside_params;
> -static struct backside_pid_state	backside_state;
> -static struct drives_pid_state		drives_state;
> -static struct dimm_pid_state		dimms_state;
> -static struct slots_pid_state		slots_state;
> -static int				state;
> -static int				cpu_count;
> -static int				cpu_pid_type;
> -static struct task_struct		*ctrl_task;
> -static struct completion		ctrl_complete;
> -static int				critical_state;
> -static int				rackmac;
> -static s32				dimm_output_clamp;
> -static int 				fcu_rpm_shift;
> -static int				fcu_tickle_ticks;
> -static DEFINE_MUTEX(driver_lock);
> -
> -/*
> - * We have 3 types of CPU PID control. One is "split" old style control
> - * for intake & exhaust fans, the other is "combined" control for both
> - * CPUs that also deals with the pumps when present. To be "compatible"
> - * with OS X at this point, we only use "COMBINED" on the machines that
> - * are identified as having the pumps (though that identification is at
> - * least dodgy). Ultimately, we could probably switch completely to this
> - * algorithm provided we hack it to deal with the UP case
> - */
> -#define CPU_PID_TYPE_SPLIT	0
> -#define CPU_PID_TYPE_COMBINED	1
> -#define CPU_PID_TYPE_RACKMAC	2
> -
> -/*
> - * This table describes all fans in the FCU. The "id" and "type" values
> - * are defaults valid for all earlier machines. Newer machines will
> - * eventually override the table content based on the device-tree
> - */
> -struct fcu_fan_table
> -{
> -	char*	loc;	/* location code */
> -	int	type;	/* 0 = rpm, 1 = pwm, 2 = pump */
> -	int	id;	/* id or -1 */
> -};
> -
> -#define FCU_FAN_RPM		0
> -#define FCU_FAN_PWM		1
> -
> -#define FCU_FAN_ABSENT_ID	-1
> -
> -#define FCU_FAN_COUNT		ARRAY_SIZE(fcu_fans)
> -
> -struct fcu_fan_table	fcu_fans[] = {
> -	[BACKSIDE_FAN_PWM_INDEX] = {
> -		.loc	= "BACKSIDE,SYS CTRLR FAN",
> -		.type	= FCU_FAN_PWM,
> -		.id	= BACKSIDE_FAN_PWM_DEFAULT_ID,
> -	},
> -	[DRIVES_FAN_RPM_INDEX] = {
> -		.loc	= "DRIVE BAY",
> -		.type	= FCU_FAN_RPM,
> -		.id	= DRIVES_FAN_RPM_DEFAULT_ID,
> -	},
> -	[SLOTS_FAN_PWM_INDEX] = {
> -		.loc	= "SLOT,PCI FAN",
> -		.type	= FCU_FAN_PWM,
> -		.id	= SLOTS_FAN_PWM_DEFAULT_ID,
> -	},
> -	[CPUA_INTAKE_FAN_RPM_INDEX] = {
> -		.loc	= "CPU A INTAKE",
> -		.type	= FCU_FAN_RPM,
> -		.id	= CPUA_INTAKE_FAN_RPM_DEFAULT_ID,
> -	},
> -	[CPUA_EXHAUST_FAN_RPM_INDEX] = {
> -		.loc	= "CPU A EXHAUST",
> -		.type	= FCU_FAN_RPM,
> -		.id	= CPUA_EXHAUST_FAN_RPM_DEFAULT_ID,
> -	},
> -	[CPUB_INTAKE_FAN_RPM_INDEX] = {
> -		.loc	= "CPU B INTAKE",
> -		.type	= FCU_FAN_RPM,
> -		.id	= CPUB_INTAKE_FAN_RPM_DEFAULT_ID,
> -	},
> -	[CPUB_EXHAUST_FAN_RPM_INDEX] = {
> -		.loc	= "CPU B EXHAUST",
> -		.type	= FCU_FAN_RPM,
> -		.id	= CPUB_EXHAUST_FAN_RPM_DEFAULT_ID,
> -	},
> -	/* pumps aren't present by default, have to be looked up in the
> -	 * device-tree
> -	 */
> -	[CPUA_PUMP_RPM_INDEX] = {
> -		.loc	= "CPU A PUMP",
> -		.type	= FCU_FAN_RPM,		
> -		.id	= FCU_FAN_ABSENT_ID,
> -	},
> -	[CPUB_PUMP_RPM_INDEX] = {
> -		.loc	= "CPU B PUMP",
> -		.type	= FCU_FAN_RPM,
> -		.id	= FCU_FAN_ABSENT_ID,
> -	},
> -	/* Xserve fans */
> -	[CPU_A1_FAN_RPM_INDEX] = {
> -		.loc	= "CPU A 1",
> -		.type	= FCU_FAN_RPM,
> -		.id	= FCU_FAN_ABSENT_ID,
> -	},
> -	[CPU_A2_FAN_RPM_INDEX] = {
> -		.loc	= "CPU A 2",
> -		.type	= FCU_FAN_RPM,
> -		.id	= FCU_FAN_ABSENT_ID,
> -	},
> -	[CPU_A3_FAN_RPM_INDEX] = {
> -		.loc	= "CPU A 3",
> -		.type	= FCU_FAN_RPM,
> -		.id	= FCU_FAN_ABSENT_ID,
> -	},
> -	[CPU_B1_FAN_RPM_INDEX] = {
> -		.loc	= "CPU B 1",
> -		.type	= FCU_FAN_RPM,
> -		.id	= FCU_FAN_ABSENT_ID,
> -	},
> -	[CPU_B2_FAN_RPM_INDEX] = {
> -		.loc	= "CPU B 2",
> -		.type	= FCU_FAN_RPM,
> -		.id	= FCU_FAN_ABSENT_ID,
> -	},
> -	[CPU_B3_FAN_RPM_INDEX] = {
> -		.loc	= "CPU B 3",
> -		.type	= FCU_FAN_RPM,
> -		.id	= FCU_FAN_ABSENT_ID,
> -	},
> -};
> -
> -static struct i2c_driver therm_pm72_driver;
> -
> -/*
> - * Utility function to create an i2c_client structure and
> - * attach it to one of u3 adapters
> - */
> -static struct i2c_client *attach_i2c_chip(int id, const char *name)
> -{
> -	struct i2c_client *clt;
> -	struct i2c_adapter *adap;
> -	struct i2c_board_info info;
> -
> -	if (id & 0x200)
> -		adap = k2;
> -	else if (id & 0x100)
> -		adap = u3_1;
> -	else
> -		adap = u3_0;
> -	if (adap == NULL)
> -		return NULL;
> -
> -	memset(&info, 0, sizeof(struct i2c_board_info));
> -	info.addr = (id >> 1) & 0x7f;
> -	strlcpy(info.type, "therm_pm72", I2C_NAME_SIZE);
> -	clt = i2c_new_device(adap, &info);
> -	if (!clt) {
> -		printk(KERN_ERR "therm_pm72: Failed to attach to i2c ID 0x%x\n", id);
> -		return NULL;
> -	}
> -
> -	/*
> -	 * Let i2c-core delete that device on driver removal.
> -	 * This is safe because i2c-core holds the core_lock mutex for us.
> -	 */
> -	list_add_tail(&clt->detected, &therm_pm72_driver.clients);
> -	return clt;
> -}
> -
> -/*
> - * Here are the i2c chip access wrappers
> - */
> -
> -static void initialize_adc(struct cpu_pid_state *state)
> -{
> -	int rc;
> -	u8 buf[2];
> -
> -	/* Read ADC the configuration register and cache it. We
> -	 * also make sure Config2 contains proper values, I've seen
> -	 * cases where we got stale grabage in there, thus preventing
> -	 * proper reading of conv. values
> -	 */
> -
> -	/* Clear Config2 */
> -	buf[0] = 5;
> -	buf[1] = 0;
> -	i2c_master_send(state->monitor, buf, 2);
> -
> -	/* Read & cache Config1 */
> -	buf[0] = 1;
> -	rc = i2c_master_send(state->monitor, buf, 1);
> -	if (rc > 0) {
> -		rc = i2c_master_recv(state->monitor, buf, 1);
> -		if (rc > 0) {
> -			state->adc_config = buf[0];
> -			DBG("ADC config reg: %02x\n", state->adc_config);
> -			/* Disable shutdown mode */
> -		       	state->adc_config &= 0xfe;
> -			buf[0] = 1;
> -			buf[1] = state->adc_config;
> -			rc = i2c_master_send(state->monitor, buf, 2);
> -		}
> -	}
> -	if (rc <= 0)
> -		printk(KERN_ERR "therm_pm72: Error reading ADC config"
> -		       " register !\n");
> -}
> -
> -static int read_smon_adc(struct cpu_pid_state *state, int chan)
> -{
> -	int rc, data, tries = 0;
> -	u8 buf[2];
> -
> -	for (;;) {
> -		/* Set channel */
> -		buf[0] = 1;
> -		buf[1] = (state->adc_config & 0x1f) | (chan << 5);
> -		rc = i2c_master_send(state->monitor, buf, 2);
> -		if (rc <= 0)
> -			goto error;
> -		/* Wait for conversion */
> -		msleep(1);
> -		/* Switch to data register */
> -		buf[0] = 4;
> -		rc = i2c_master_send(state->monitor, buf, 1);
> -		if (rc <= 0)
> -			goto error;
> -		/* Read result */
> -		rc = i2c_master_recv(state->monitor, buf, 2);
> -		if (rc < 0)
> -			goto error;
> -		data = ((u16)buf[0]) << 8 | (u16)buf[1];
> -		return data >> 6;
> -	error:
> -		DBG("Error reading ADC, retrying...\n");
> -		if (++tries > 10) {
> -			printk(KERN_ERR "therm_pm72: Error reading ADC !\n");
> -			return -1;
> -		}
> -		msleep(10);
> -	}
> -}
> -
> -static int read_lm87_reg(struct i2c_client * chip, int reg)
> -{
> -	int rc, tries = 0;
> -	u8 buf;
> -
> -	for (;;) {
> -		/* Set address */
> -		buf = (u8)reg;
> -		rc = i2c_master_send(chip, &buf, 1);
> -		if (rc <= 0)
> -			goto error;
> -		rc = i2c_master_recv(chip, &buf, 1);
> -		if (rc <= 0)
> -			goto error;
> -		return (int)buf;
> -	error:
> -		DBG("Error reading LM87, retrying...\n");
> -		if (++tries > 10) {
> -			printk(KERN_ERR "therm_pm72: Error reading LM87 !\n");
> -			return -1;
> -		}
> -		msleep(10);
> -	}
> -}
> -
> -static int fan_read_reg(int reg, unsigned char *buf, int nb)
> -{
> -	int tries, nr, nw;
> -
> -	buf[0] = reg;
> -	tries = 0;
> -	for (;;) {
> -		nw = i2c_master_send(fcu, buf, 1);
> -		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
> -			break;
> -		msleep(10);
> -		++tries;
> -	}
> -	if (nw <= 0) {
> -		printk(KERN_ERR "Failure writing address to FCU: %d", nw);
> -		return -EIO;
> -	}
> -	tries = 0;
> -	for (;;) {
> -		nr = i2c_master_recv(fcu, buf, nb);
> -		if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100)
> -			break;
> -		msleep(10);
> -		++tries;
> -	}
> -	if (nr <= 0)
> -		printk(KERN_ERR "Failure reading data from FCU: %d", nw);
> -	return nr;
> -}
> -
> -static int fan_write_reg(int reg, const unsigned char *ptr, int nb)
> -{
> -	int tries, nw;
> -	unsigned char buf[16];
> -
> -	buf[0] = reg;
> -	memcpy(buf+1, ptr, nb);
> -	++nb;
> -	tries = 0;
> -	for (;;) {
> -		nw = i2c_master_send(fcu, buf, nb);
> -		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
> -			break;
> -		msleep(10);
> -		++tries;
> -	}
> -	if (nw < 0)
> -		printk(KERN_ERR "Failure writing to FCU: %d", nw);
> -	return nw;
> -}
> -
> -static int start_fcu(void)
> -{
> -	unsigned char buf = 0xff;
> -	int rc;
> -
> -	rc = fan_write_reg(0xe, &buf, 1);
> -	if (rc < 0)
> -		return -EIO;
> -	rc = fan_write_reg(0x2e, &buf, 1);
> -	if (rc < 0)
> -		return -EIO;
> -	rc = fan_read_reg(0, &buf, 1);
> -	if (rc < 0)
> -		return -EIO;
> -	fcu_rpm_shift = (buf == 1) ? 2 : 3;
> -	printk(KERN_DEBUG "FCU Initialized, RPM fan shift is %d\n",
> -	       fcu_rpm_shift);
> -
> -	return 0;
> -}
> -
> -static int set_rpm_fan(int fan_index, int rpm)
> -{
> -	unsigned char buf[2];
> -	int rc, id, min, max;
> -
> -	if (fcu_fans[fan_index].type != FCU_FAN_RPM)
> -		return -EINVAL;
> -	id = fcu_fans[fan_index].id; 
> -	if (id == FCU_FAN_ABSENT_ID)
> -		return -EINVAL;
> -
> -	min = 2400 >> fcu_rpm_shift;
> -	max = 56000 >> fcu_rpm_shift;
> -
> -	if (rpm < min)
> -		rpm = min;
> -	else if (rpm > max)
> -		rpm = max;
> -	buf[0] = rpm >> (8 - fcu_rpm_shift);
> -	buf[1] = rpm << fcu_rpm_shift;
> -	rc = fan_write_reg(0x10 + (id * 2), buf, 2);
> -	if (rc < 0)
> -		return -EIO;
> -	return 0;
> -}
> -
> -static int get_rpm_fan(int fan_index, int programmed)
> -{
> -	unsigned char failure;
> -	unsigned char active;
> -	unsigned char buf[2];
> -	int rc, id, reg_base;
> -
> -	if (fcu_fans[fan_index].type != FCU_FAN_RPM)
> -		return -EINVAL;
> -	id = fcu_fans[fan_index].id; 
> -	if (id == FCU_FAN_ABSENT_ID)
> -		return -EINVAL;
> -
> -	rc = fan_read_reg(0xb, &failure, 1);
> -	if (rc != 1)
> -		return -EIO;
> -	if ((failure & (1 << id)) != 0)
> -		return -EFAULT;
> -	rc = fan_read_reg(0xd, &active, 1);
> -	if (rc != 1)
> -		return -EIO;
> -	if ((active & (1 << id)) == 0)
> -		return -ENXIO;
> -
> -	/* Programmed value or real current speed */
> -	reg_base = programmed ? 0x10 : 0x11;
> -	rc = fan_read_reg(reg_base + (id * 2), buf, 2);
> -	if (rc != 2)
> -		return -EIO;
> -
> -	return (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift;
> -}
> -
> -static int set_pwm_fan(int fan_index, int pwm)
> -{
> -	unsigned char buf[2];
> -	int rc, id;
> -
> -	if (fcu_fans[fan_index].type != FCU_FAN_PWM)
> -		return -EINVAL;
> -	id = fcu_fans[fan_index].id; 
> -	if (id == FCU_FAN_ABSENT_ID)
> -		return -EINVAL;
> -
> -	if (pwm < 10)
> -		pwm = 10;
> -	else if (pwm > 100)
> -		pwm = 100;
> -	pwm = (pwm * 2559) / 1000;
> -	buf[0] = pwm;
> -	rc = fan_write_reg(0x30 + (id * 2), buf, 1);
> -	if (rc < 0)
> -		return rc;
> -	return 0;
> -}
> -
> -static int get_pwm_fan(int fan_index)
> -{
> -	unsigned char failure;
> -	unsigned char active;
> -	unsigned char buf[2];
> -	int rc, id;
> -
> -	if (fcu_fans[fan_index].type != FCU_FAN_PWM)
> -		return -EINVAL;
> -	id = fcu_fans[fan_index].id; 
> -	if (id == FCU_FAN_ABSENT_ID)
> -		return -EINVAL;
> -
> -	rc = fan_read_reg(0x2b, &failure, 1);
> -	if (rc != 1)
> -		return -EIO;
> -	if ((failure & (1 << id)) != 0)
> -		return -EFAULT;
> -	rc = fan_read_reg(0x2d, &active, 1);
> -	if (rc != 1)
> -		return -EIO;
> -	if ((active & (1 << id)) == 0)
> -		return -ENXIO;
> -
> -	/* Programmed value or real current speed */
> -	rc = fan_read_reg(0x30 + (id * 2), buf, 1);
> -	if (rc != 1)
> -		return -EIO;
> -
> -	return (buf[0] * 1000) / 2559;
> -}
> -
> -static void tickle_fcu(void)
> -{
> -	int pwm;
> -
> -	pwm = get_pwm_fan(SLOTS_FAN_PWM_INDEX);
> -
> -	DBG("FCU Tickle, slots fan is: %d\n", pwm);
> -	if (pwm < 0)
> -		pwm = 100;
> -
> -	if (!rackmac) {
> -		pwm = SLOTS_FAN_DEFAULT_PWM;
> -	} else if (pwm < SLOTS_PID_OUTPUT_MIN)
> -		pwm = SLOTS_PID_OUTPUT_MIN;
> -
> -	/* That is hopefully enough to make the FCU happy */
> -	set_pwm_fan(SLOTS_FAN_PWM_INDEX, pwm);
> -}
> -
> -
> -/*
> - * Utility routine to read the CPU calibration EEPROM data
> - * from the device-tree
> - */
> -static int read_eeprom(int cpu, struct mpu_data *out)
> -{
> -	struct device_node *np;
> -	char nodename[64];
> -	const u8 *data;
> -	int len;
> -
> -	/* prom.c routine for finding a node by path is a bit brain dead
> -	 * and requires exact @xxx unit numbers. This is a bit ugly but
> -	 * will work for these machines
> -	 */
> -	sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0);
> -	np = of_find_node_by_path(nodename);
> -	if (np == NULL) {
> -		printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid node from device-tree\n");
> -		return -ENODEV;
> -	}
> -	data = of_get_property(np, "cpuid", &len);
> -	if (data == NULL) {
> -		printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid property from device-tree\n");
> -		of_node_put(np);
> -		return -ENODEV;
> -	}
> -	memcpy(out, data, sizeof(struct mpu_data));
> -	of_node_put(np);
> -	
> -	return 0;
> -}
> -
> -static void fetch_cpu_pumps_minmax(void)
> -{
> -	struct cpu_pid_state *state0 = &processor_state[0];
> -	struct cpu_pid_state *state1 = &processor_state[1];
> -	u16 pump_min = 0, pump_max = 0xffff;
> -	u16 tmp[4];
> -
> -	/* Try to fetch pumps min/max infos from eeprom */
> -
> -	memcpy(&tmp, &state0->mpu.processor_part_num, 8);
> -	if (tmp[0] != 0xffff && tmp[1] != 0xffff) {
> -		pump_min = max(pump_min, tmp[0]);
> -		pump_max = min(pump_max, tmp[1]);
> -	}
> -	if (tmp[2] != 0xffff && tmp[3] != 0xffff) {
> -		pump_min = max(pump_min, tmp[2]);
> -		pump_max = min(pump_max, tmp[3]);
> -	}
> -
> -	/* Double check the values, this _IS_ needed as the EEPROM on
> -	 * some dual 2.5Ghz G5s seem, at least, to have both min & max
> -	 * same to the same value ... (grrrr)
> -	 */
> -	if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) {
> -		pump_min = CPU_PUMP_OUTPUT_MIN;
> -		pump_max = CPU_PUMP_OUTPUT_MAX;
> -	}
> -
> -	state0->pump_min = state1->pump_min = pump_min;
> -	state0->pump_max = state1->pump_max = pump_max;
> -}
> -
> -/* 
> - * Now, unfortunately, sysfs doesn't give us a nice void * we could
> - * pass around to the attribute functions, so we don't really have
> - * choice but implement a bunch of them...
> - *
> - * That sucks a bit, we take the lock because FIX32TOPRINT evaluates
> - * the input twice... I accept patches :)
> - */
> -#define BUILD_SHOW_FUNC_FIX(name, data)				\
> -static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)	\
> -{								\
> -	ssize_t r;						\
> -	mutex_lock(&driver_lock);					\
> -	r = sprintf(buf, "%d.%03d", FIX32TOPRINT(data));	\
> -	mutex_unlock(&driver_lock);					\
> -	return r;						\
> -}
> -#define BUILD_SHOW_FUNC_INT(name, data)				\
> -static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)	\
> -{								\
> -	return sprintf(buf, "%d", data);			\
> -}
> -
> -BUILD_SHOW_FUNC_FIX(cpu0_temperature, processor_state[0].last_temp)
> -BUILD_SHOW_FUNC_FIX(cpu0_voltage, processor_state[0].voltage)
> -BUILD_SHOW_FUNC_FIX(cpu0_current, processor_state[0].current_a)
> -BUILD_SHOW_FUNC_INT(cpu0_exhaust_fan_rpm, processor_state[0].rpm)
> -BUILD_SHOW_FUNC_INT(cpu0_intake_fan_rpm, processor_state[0].intake_rpm)
> -
> -BUILD_SHOW_FUNC_FIX(cpu1_temperature, processor_state[1].last_temp)
> -BUILD_SHOW_FUNC_FIX(cpu1_voltage, processor_state[1].voltage)
> -BUILD_SHOW_FUNC_FIX(cpu1_current, processor_state[1].current_a)
> -BUILD_SHOW_FUNC_INT(cpu1_exhaust_fan_rpm, processor_state[1].rpm)
> -BUILD_SHOW_FUNC_INT(cpu1_intake_fan_rpm, processor_state[1].intake_rpm)
> -
> -BUILD_SHOW_FUNC_FIX(backside_temperature, backside_state.last_temp)
> -BUILD_SHOW_FUNC_INT(backside_fan_pwm, backside_state.pwm)
> -
> -BUILD_SHOW_FUNC_FIX(drives_temperature, drives_state.last_temp)
> -BUILD_SHOW_FUNC_INT(drives_fan_rpm, drives_state.rpm)
> -
> -BUILD_SHOW_FUNC_FIX(slots_temperature, slots_state.last_temp)
> -BUILD_SHOW_FUNC_INT(slots_fan_pwm, slots_state.pwm)
> -
> -BUILD_SHOW_FUNC_FIX(dimms_temperature, dimms_state.last_temp)
> -
> -static DEVICE_ATTR(cpu0_temperature,S_IRUGO,show_cpu0_temperature,NULL);
> -static DEVICE_ATTR(cpu0_voltage,S_IRUGO,show_cpu0_voltage,NULL);
> -static DEVICE_ATTR(cpu0_current,S_IRUGO,show_cpu0_current,NULL);
> -static DEVICE_ATTR(cpu0_exhaust_fan_rpm,S_IRUGO,show_cpu0_exhaust_fan_rpm,NULL);
> -static DEVICE_ATTR(cpu0_intake_fan_rpm,S_IRUGO,show_cpu0_intake_fan_rpm,NULL);
> -
> -static DEVICE_ATTR(cpu1_temperature,S_IRUGO,show_cpu1_temperature,NULL);
> -static DEVICE_ATTR(cpu1_voltage,S_IRUGO,show_cpu1_voltage,NULL);
> -static DEVICE_ATTR(cpu1_current,S_IRUGO,show_cpu1_current,NULL);
> -static DEVICE_ATTR(cpu1_exhaust_fan_rpm,S_IRUGO,show_cpu1_exhaust_fan_rpm,NULL);
> -static DEVICE_ATTR(cpu1_intake_fan_rpm,S_IRUGO,show_cpu1_intake_fan_rpm,NULL);
> -
> -static DEVICE_ATTR(backside_temperature,S_IRUGO,show_backside_temperature,NULL);
> -static DEVICE_ATTR(backside_fan_pwm,S_IRUGO,show_backside_fan_pwm,NULL);
> -
> -static DEVICE_ATTR(drives_temperature,S_IRUGO,show_drives_temperature,NULL);
> -static DEVICE_ATTR(drives_fan_rpm,S_IRUGO,show_drives_fan_rpm,NULL);
> -
> -static DEVICE_ATTR(slots_temperature,S_IRUGO,show_slots_temperature,NULL);
> -static DEVICE_ATTR(slots_fan_pwm,S_IRUGO,show_slots_fan_pwm,NULL);
> -
> -static DEVICE_ATTR(dimms_temperature,S_IRUGO,show_dimms_temperature,NULL);
> -
> -/*
> - * CPUs fans control loop
> - */
> -
> -static int do_read_one_cpu_values(struct cpu_pid_state *state, s32 *temp, s32 *power)
> -{
> -	s32 ltemp, volts, amps;
> -	int index, rc = 0;
> -
> -	/* Default (in case of error) */
> -	*temp = state->cur_temp;
> -	*power = state->cur_power;
> -
> -	if (cpu_pid_type == CPU_PID_TYPE_RACKMAC)
> -		index = (state->index == 0) ?
> -			CPU_A1_FAN_RPM_INDEX : CPU_B1_FAN_RPM_INDEX;
> -	else
> -		index = (state->index == 0) ?
> -			CPUA_EXHAUST_FAN_RPM_INDEX : CPUB_EXHAUST_FAN_RPM_INDEX;
> -
> -	/* Read current fan status */
> -	rc = get_rpm_fan(index, !RPM_PID_USE_ACTUAL_SPEED);
> -	if (rc < 0) {
> -		/* XXX What do we do now ? Nothing for now, keep old value, but
> -		 * return error upstream
> -		 */
> -		DBG("  cpu %d, fan reading error !\n", state->index);
> -	} else {
> -		state->rpm = rc;
> -		DBG("  cpu %d, exhaust RPM: %d\n", state->index, state->rpm);
> -	}
> -
> -	/* Get some sensor readings and scale it */
> -	ltemp = read_smon_adc(state, 1);
> -	if (ltemp == -1) {
> -		/* XXX What do we do now ? */
> -		state->overtemp++;
> -		if (rc == 0)
> -			rc = -EIO;
> -		DBG("  cpu %d, temp reading error !\n", state->index);
> -	} else {
> -		/* Fixup temperature according to diode calibration
> -		 */
> -		DBG("  cpu %d, temp raw: %04x, m_diode: %04x, b_diode: %04x\n",
> -		    state->index,
> -		    ltemp, state->mpu.mdiode, state->mpu.bdiode);
> -		*temp = ((s32)ltemp * (s32)state->mpu.mdiode + ((s32)state->mpu.bdiode << 12)) >> 2;
> -		state->last_temp = *temp;
> -		DBG("  temp: %d.%03d\n", FIX32TOPRINT((*temp)));
> -	}
> -
> -	/*
> -	 * Read voltage & current and calculate power
> -	 */
> -	volts = read_smon_adc(state, 3);
> -	amps = read_smon_adc(state, 4);
> -
> -	/* Scale voltage and current raw sensor values according to fixed scales
> -	 * obtained in Darwin and calculate power from I and V
> -	 */
> -	volts *= ADC_CPU_VOLTAGE_SCALE;
> -	amps *= ADC_CPU_CURRENT_SCALE;
> -	*power = (((u64)volts) * ((u64)amps)) >> 16;
> -	state->voltage = volts;
> -	state->current_a = amps;
> -	state->last_power = *power;
> -
> -	DBG("  cpu %d, current: %d.%03d, voltage: %d.%03d, power: %d.%03d W\n",
> -	    state->index, FIX32TOPRINT(state->current_a),
> -	    FIX32TOPRINT(state->voltage), FIX32TOPRINT(*power));
> -
> -	return 0;
> -}
> -
> -static void do_cpu_pid(struct cpu_pid_state *state, s32 temp, s32 power)
> -{
> -	s32 power_target, integral, derivative, proportional, adj_in_target, sval;
> -	s64 integ_p, deriv_p, prop_p, sum; 
> -	int i;
> -
> -	/* Calculate power target value (could be done once for all)
> -	 * and convert to a 16.16 fp number
> -	 */
> -	power_target = ((u32)(state->mpu.pmaxh - state->mpu.padjmax)) << 16;
> -	DBG("  power target: %d.%03d, error: %d.%03d\n",
> -	    FIX32TOPRINT(power_target), FIX32TOPRINT(power_target - power));
> -
> -	/* Store temperature and power in history array */
> -	state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE;
> -	state->temp_history[state->cur_temp] = temp;
> -	state->cur_power = (state->cur_power + 1) % state->count_power;
> -	state->power_history[state->cur_power] = power;
> -	state->error_history[state->cur_power] = power_target - power;
> -	
> -	/* If first loop, fill the history table */
> -	if (state->first) {
> -		for (i = 0; i < (state->count_power - 1); i++) {
> -			state->cur_power = (state->cur_power + 1) % state->count_power;
> -			state->power_history[state->cur_power] = power;
> -			state->error_history[state->cur_power] = power_target - power;
> -		}
> -		for (i = 0; i < (CPU_TEMP_HISTORY_SIZE - 1); i++) {
> -			state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE;
> -			state->temp_history[state->cur_temp] = temp;			
> -		}
> -		state->first = 0;
> -	}
> -
> -	/* Calculate the integral term normally based on the "power" values */
> -	sum = 0;
> -	integral = 0;
> -	for (i = 0; i < state->count_power; i++)
> -		integral += state->error_history[i];
> -	integral *= CPU_PID_INTERVAL;
> -	DBG("  integral: %08x\n", integral);
> -
> -	/* Calculate the adjusted input (sense value).
> -	 *   G_r is 12.20
> -	 *   integ is 16.16
> -	 *   so the result is 28.36
> -	 *
> -	 * input target is mpu.ttarget, input max is mpu.tmax
> -	 */
> -	integ_p = ((s64)state->mpu.pid_gr) * (s64)integral;
> -	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
> -	sval = (state->mpu.tmax << 16) - ((integ_p >> 20) & 0xffffffff);
> -	adj_in_target = (state->mpu.ttarget << 16);
> -	if (adj_in_target > sval)
> -		adj_in_target = sval;
> -	DBG("   adj_in_target: %d.%03d, ttarget: %d\n", FIX32TOPRINT(adj_in_target),
> -	    state->mpu.ttarget);
> -
> -	/* Calculate the derivative term */
> -	derivative = state->temp_history[state->cur_temp] -
> -		state->temp_history[(state->cur_temp + CPU_TEMP_HISTORY_SIZE - 1)
> -				    % CPU_TEMP_HISTORY_SIZE];
> -	derivative /= CPU_PID_INTERVAL;
> -	deriv_p = ((s64)state->mpu.pid_gd) * (s64)derivative;
> -	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
> -	sum += deriv_p;
> -
> -	/* Calculate the proportional term */
> -	proportional = temp - adj_in_target;
> -	prop_p = ((s64)state->mpu.pid_gp) * (s64)proportional;
> -	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
> -	sum += prop_p;
> -
> -	/* Scale sum */
> -	sum >>= 36;
> -
> -	DBG("   sum: %d\n", (int)sum);
> -	state->rpm += (s32)sum;
> -}
> -
> -static void do_monitor_cpu_combined(void)
> -{
> -	struct cpu_pid_state *state0 = &processor_state[0];
> -	struct cpu_pid_state *state1 = &processor_state[1];
> -	s32 temp0, power0, temp1, power1;
> -	s32 temp_combi, power_combi;
> -	int rc, intake, pump;
> -
> -	rc = do_read_one_cpu_values(state0, &temp0, &power0);
> -	if (rc < 0) {
> -		/* XXX What do we do now ? */
> -	}
> -	state1->overtemp = 0;
> -	rc = do_read_one_cpu_values(state1, &temp1, &power1);
> -	if (rc < 0) {
> -		/* XXX What do we do now ? */
> -	}
> -	if (state1->overtemp)
> -		state0->overtemp++;
> -
> -	temp_combi = max(temp0, temp1);
> -	power_combi = max(power0, power1);
> -
> -	/* Check tmax, increment overtemp if we are there. At tmax+8, we go
> -	 * full blown immediately and try to trigger a shutdown
> -	 */
> -	if (temp_combi >= ((state0->mpu.tmax + 8) << 16)) {
> -		printk(KERN_WARNING "Warning ! Temperature way above maximum (%d) !\n",
> -		       temp_combi >> 16);
> -		state0->overtemp += CPU_MAX_OVERTEMP / 4;
> -	} else if (temp_combi > (state0->mpu.tmax << 16)) {
> -		state0->overtemp++;
> -		printk(KERN_WARNING "Temperature %d above max %d. overtemp %d\n",
> -		       temp_combi >> 16, state0->mpu.tmax, state0->overtemp);
> -	} else {
> -		if (state0->overtemp)
> -			printk(KERN_WARNING "Temperature back down to %d\n",
> -			       temp_combi >> 16);
> -		state0->overtemp = 0;
> -	}
> -	if (state0->overtemp >= CPU_MAX_OVERTEMP)
> -		critical_state = 1;
> -	if (state0->overtemp > 0) {
> -		state0->rpm = state0->mpu.rmaxn_exhaust_fan;
> -		state0->intake_rpm = intake = state0->mpu.rmaxn_intake_fan;
> -		pump = state0->pump_max;
> -		goto do_set_fans;
> -	}
> -
> -	/* Do the PID */
> -	do_cpu_pid(state0, temp_combi, power_combi);
> -
> -	/* Range check */
> -	state0->rpm = max(state0->rpm, (int)state0->mpu.rminn_exhaust_fan);
> -	state0->rpm = min(state0->rpm, (int)state0->mpu.rmaxn_exhaust_fan);
> -
> -	/* Calculate intake fan speed */
> -	intake = (state0->rpm * CPU_INTAKE_SCALE) >> 16;
> -	intake = max(intake, (int)state0->mpu.rminn_intake_fan);
> -	intake = min(intake, (int)state0->mpu.rmaxn_intake_fan);
> -	state0->intake_rpm = intake;
> -
> -	/* Calculate pump speed */
> -	pump = (state0->rpm * state0->pump_max) /
> -		state0->mpu.rmaxn_exhaust_fan;
> -	pump = min(pump, state0->pump_max);
> -	pump = max(pump, state0->pump_min);
> -	
> - do_set_fans:
> -	/* We copy values from state 0 to state 1 for /sysfs */
> -	state1->rpm = state0->rpm;
> -	state1->intake_rpm = state0->intake_rpm;
> -
> -	DBG("** CPU %d RPM: %d Ex, %d, Pump: %d, In, overtemp: %d\n",
> -	    state1->index, (int)state1->rpm, intake, pump, state1->overtemp);
> -
> -	/* We should check for errors, shouldn't we ? But then, what
> -	 * do we do once the error occurs ? For FCU notified fan
> -	 * failures (-EFAULT) we probably want to notify userland
> -	 * some way...
> -	 */
> -	set_rpm_fan(CPUA_INTAKE_FAN_RPM_INDEX, intake);
> -	set_rpm_fan(CPUA_EXHAUST_FAN_RPM_INDEX, state0->rpm);
> -	set_rpm_fan(CPUB_INTAKE_FAN_RPM_INDEX, intake);
> -	set_rpm_fan(CPUB_EXHAUST_FAN_RPM_INDEX, state0->rpm);
> -
> -	if (fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID)
> -		set_rpm_fan(CPUA_PUMP_RPM_INDEX, pump);
> -	if (fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID)
> -		set_rpm_fan(CPUB_PUMP_RPM_INDEX, pump);
> -}
> -
> -static void do_monitor_cpu_split(struct cpu_pid_state *state)
> -{
> -	s32 temp, power;
> -	int rc, intake;
> -
> -	/* Read current fan status */
> -	rc = do_read_one_cpu_values(state, &temp, &power);
> -	if (rc < 0) {
> -		/* XXX What do we do now ? */
> -	}
> -
> -	/* Check tmax, increment overtemp if we are there. At tmax+8, we go
> -	 * full blown immediately and try to trigger a shutdown
> -	 */
> -	if (temp >= ((state->mpu.tmax + 8) << 16)) {
> -		printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum"
> -		       " (%d) !\n",
> -		       state->index, temp >> 16);
> -		state->overtemp += CPU_MAX_OVERTEMP / 4;
> -	} else if (temp > (state->mpu.tmax << 16)) {
> -		state->overtemp++;
> -		printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
> -		       state->index, temp >> 16, state->mpu.tmax, state->overtemp);
> -	} else {
> -		if (state->overtemp)
> -			printk(KERN_WARNING "CPU %d temperature back down to %d\n",
> -			       state->index, temp >> 16);
> -		state->overtemp = 0;
> -	}
> -	if (state->overtemp >= CPU_MAX_OVERTEMP)
> -		critical_state = 1;
> -	if (state->overtemp > 0) {
> -		state->rpm = state->mpu.rmaxn_exhaust_fan;
> -		state->intake_rpm = intake = state->mpu.rmaxn_intake_fan;
> -		goto do_set_fans;
> -	}
> -
> -	/* Do the PID */
> -	do_cpu_pid(state, temp, power);
> -
> -	/* Range check */
> -	state->rpm = max(state->rpm, (int)state->mpu.rminn_exhaust_fan);
> -	state->rpm = min(state->rpm, (int)state->mpu.rmaxn_exhaust_fan);
> -
> -	/* Calculate intake fan */
> -	intake = (state->rpm * CPU_INTAKE_SCALE) >> 16;
> -	intake = max(intake, (int)state->mpu.rminn_intake_fan);
> -	intake = min(intake, (int)state->mpu.rmaxn_intake_fan);
> -	state->intake_rpm = intake;
> -
> - do_set_fans:
> -	DBG("** CPU %d RPM: %d Ex, %d In, overtemp: %d\n",
> -	    state->index, (int)state->rpm, intake, state->overtemp);
> -
> -	/* We should check for errors, shouldn't we ? But then, what
> -	 * do we do once the error occurs ? For FCU notified fan
> -	 * failures (-EFAULT) we probably want to notify userland
> -	 * some way...
> -	 */
> -	if (state->index == 0) {
> -		set_rpm_fan(CPUA_INTAKE_FAN_RPM_INDEX, intake);
> -		set_rpm_fan(CPUA_EXHAUST_FAN_RPM_INDEX, state->rpm);
> -	} else {
> -		set_rpm_fan(CPUB_INTAKE_FAN_RPM_INDEX, intake);
> -		set_rpm_fan(CPUB_EXHAUST_FAN_RPM_INDEX, state->rpm);
> -	}
> -}
> -
> -static void do_monitor_cpu_rack(struct cpu_pid_state *state)
> -{
> -	s32 temp, power, fan_min;
> -	int rc;
> -
> -	/* Read current fan status */
> -	rc = do_read_one_cpu_values(state, &temp, &power);
> -	if (rc < 0) {
> -		/* XXX What do we do now ? */
> -	}
> -
> -	/* Check tmax, increment overtemp if we are there. At tmax+8, we go
> -	 * full blown immediately and try to trigger a shutdown
> -	 */
> -	if (temp >= ((state->mpu.tmax + 8) << 16)) {
> -		printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum"
> -		       " (%d) !\n",
> -		       state->index, temp >> 16);
> -		state->overtemp = CPU_MAX_OVERTEMP / 4;
> -	} else if (temp > (state->mpu.tmax << 16)) {
> -		state->overtemp++;
> -		printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
> -		       state->index, temp >> 16, state->mpu.tmax, state->overtemp);
> -	} else {
> -		if (state->overtemp)
> -			printk(KERN_WARNING "CPU %d temperature back down to %d\n",
> -			       state->index, temp >> 16);
> -		state->overtemp = 0;
> -	}
> -	if (state->overtemp >= CPU_MAX_OVERTEMP)
> -		critical_state = 1;
> -	if (state->overtemp > 0) {
> -		state->rpm = state->intake_rpm = state->mpu.rmaxn_intake_fan;
> -		goto do_set_fans;
> -	}
> -
> -	/* Do the PID */
> -	do_cpu_pid(state, temp, power);
> -
> -	/* Check clamp from dimms */
> -	fan_min = dimm_output_clamp;
> -	fan_min = max(fan_min, (int)state->mpu.rminn_intake_fan);
> -
> -	DBG(" CPU min mpu = %d, min dimm = %d\n",
> -	    state->mpu.rminn_intake_fan, dimm_output_clamp);
> -
> -	state->rpm = max(state->rpm, (int)fan_min);
> -	state->rpm = min(state->rpm, (int)state->mpu.rmaxn_intake_fan);
> -	state->intake_rpm = state->rpm;
> -
> - do_set_fans:
> -	DBG("** CPU %d RPM: %d overtemp: %d\n",
> -	    state->index, (int)state->rpm, state->overtemp);
> -
> -	/* We should check for errors, shouldn't we ? But then, what
> -	 * do we do once the error occurs ? For FCU notified fan
> -	 * failures (-EFAULT) we probably want to notify userland
> -	 * some way...
> -	 */
> -	if (state->index == 0) {
> -		set_rpm_fan(CPU_A1_FAN_RPM_INDEX, state->rpm);
> -		set_rpm_fan(CPU_A2_FAN_RPM_INDEX, state->rpm);
> -		set_rpm_fan(CPU_A3_FAN_RPM_INDEX, state->rpm);
> -	} else {
> -		set_rpm_fan(CPU_B1_FAN_RPM_INDEX, state->rpm);
> -		set_rpm_fan(CPU_B2_FAN_RPM_INDEX, state->rpm);
> -		set_rpm_fan(CPU_B3_FAN_RPM_INDEX, state->rpm);
> -	}
> -}
> -
> -/*
> - * Initialize the state structure for one CPU control loop
> - */
> -static int init_processor_state(struct cpu_pid_state *state, int index)
> -{
> -	int err;
> -
> -	state->index = index;
> -	state->first = 1;
> -	state->rpm = (cpu_pid_type == CPU_PID_TYPE_RACKMAC) ? 4000 : 1000;
> -	state->overtemp = 0;
> -	state->adc_config = 0x00;
> -
> -
> -	if (index == 0)
> -		state->monitor = attach_i2c_chip(SUPPLY_MONITOR_ID, "CPU0_monitor");
> -	else if (index == 1)
> -		state->monitor = attach_i2c_chip(SUPPLY_MONITORB_ID, "CPU1_monitor");
> -	if (state->monitor == NULL)
> -		goto fail;
> -
> -	if (read_eeprom(index, &state->mpu))
> -		goto fail;
> -
> -	state->count_power = state->mpu.tguardband;
> -	if (state->count_power > CPU_POWER_HISTORY_SIZE) {
> -		printk(KERN_WARNING "Warning ! too many power history slots\n");
> -		state->count_power = CPU_POWER_HISTORY_SIZE;
> -	}
> -	DBG("CPU %d Using %d power history entries\n", index, state->count_power);
> -
> -	if (index == 0) {
> -		err = device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
> -		err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
> -		err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_current);
> -		err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
> -		err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
> -	} else {
> -		err = device_create_file(&of_dev->dev, &dev_attr_cpu1_temperature);
> -		err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_voltage);
> -		err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_current);
> -		err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
> -		err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
> -	}
> -	if (err)
> -		printk(KERN_WARNING "Failed to create some of the attribute"
> -			"files for CPU %d\n", index);
> -
> -	return 0;
> - fail:
> -	state->monitor = NULL;
> -	
> -	return -ENODEV;
> -}
> -
> -/*
> - * Dispose of the state data for one CPU control loop
> - */
> -static void dispose_processor_state(struct cpu_pid_state *state)
> -{
> -	if (state->monitor == NULL)
> -		return;
> -
> -	if (state->index == 0) {
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu0_temperature);
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu0_voltage);
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu0_current);
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
> -	} else {
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu1_temperature);
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu1_voltage);
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu1_current);
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
> -		device_remove_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
> -	}
> -
> -	state->monitor = NULL;
> -}
> -
> -/*
> - * Motherboard backside & U3 heatsink fan control loop
> - */
> -static void do_monitor_backside(struct backside_pid_state *state)
> -{
> -	s32 temp, integral, derivative, fan_min;
> -	s64 integ_p, deriv_p, prop_p, sum; 
> -	int i, rc;
> -
> -	if (--state->ticks != 0)
> -		return;
> -	state->ticks = backside_params.interval;
> -
> -	DBG("backside:\n");
> -
> -	/* Check fan status */
> -	rc = get_pwm_fan(BACKSIDE_FAN_PWM_INDEX);
> -	if (rc < 0) {
> -		printk(KERN_WARNING "Error %d reading backside fan !\n", rc);
> -		/* XXX What do we do now ? */
> -	} else
> -		state->pwm = rc;
> -	DBG("  current pwm: %d\n", state->pwm);
> -
> -	/* Get some sensor readings */
> -	temp = i2c_smbus_read_byte_data(state->monitor, MAX6690_EXT_TEMP) << 16;
> -	state->last_temp = temp;
> -	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
> -	    FIX32TOPRINT(backside_params.input_target));
> -
> -	/* Store temperature and error in history array */
> -	state->cur_sample = (state->cur_sample + 1) % BACKSIDE_PID_HISTORY_SIZE;
> -	state->sample_history[state->cur_sample] = temp;
> -	state->error_history[state->cur_sample] = temp - backside_params.input_target;
> -	
> -	/* If first loop, fill the history table */
> -	if (state->first) {
> -		for (i = 0; i < (BACKSIDE_PID_HISTORY_SIZE - 1); i++) {
> -			state->cur_sample = (state->cur_sample + 1) %
> -				BACKSIDE_PID_HISTORY_SIZE;
> -			state->sample_history[state->cur_sample] = temp;
> -			state->error_history[state->cur_sample] =
> -				temp - backside_params.input_target;
> -		}
> -		state->first = 0;
> -	}
> -
> -	/* Calculate the integral term */
> -	sum = 0;
> -	integral = 0;
> -	for (i = 0; i < BACKSIDE_PID_HISTORY_SIZE; i++)
> -		integral += state->error_history[i];
> -	integral *= backside_params.interval;
> -	DBG("  integral: %08x\n", integral);
> -	integ_p = ((s64)backside_params.G_r) * (s64)integral;
> -	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
> -	sum += integ_p;
> -
> -	/* Calculate the derivative term */
> -	derivative = state->error_history[state->cur_sample] -
> -		state->error_history[(state->cur_sample + BACKSIDE_PID_HISTORY_SIZE - 1)
> -				    % BACKSIDE_PID_HISTORY_SIZE];
> -	derivative /= backside_params.interval;
> -	deriv_p = ((s64)backside_params.G_d) * (s64)derivative;
> -	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
> -	sum += deriv_p;
> -
> -	/* Calculate the proportional term */
> -	prop_p = ((s64)backside_params.G_p) * (s64)(state->error_history[state->cur_sample]);
> -	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
> -	sum += prop_p;
> -
> -	/* Scale sum */
> -	sum >>= 36;
> -
> -	DBG("   sum: %d\n", (int)sum);
> -	if (backside_params.additive)
> -		state->pwm += (s32)sum;
> -	else
> -		state->pwm = sum;
> -
> -	/* Check for clamp */
> -	fan_min = (dimm_output_clamp * 100) / 14000;
> -	fan_min = max(fan_min, backside_params.output_min);
> -
> -	state->pwm = max(state->pwm, fan_min);
> -	state->pwm = min(state->pwm, backside_params.output_max);
> -
> -	DBG("** BACKSIDE PWM: %d\n", (int)state->pwm);
> -	set_pwm_fan(BACKSIDE_FAN_PWM_INDEX, state->pwm);
> -}
> -
> -/*
> - * Initialize the state structure for the backside fan control loop
> - */
> -static int init_backside_state(struct backside_pid_state *state)
> -{
> -	struct device_node *u3;
> -	int u3h = 1; /* conservative by default */
> -	int err;
> -
> -	/*
> -	 * There are different PID params for machines with U3 and machines
> -	 * with U3H, pick the right ones now
> -	 */
> -	u3 = of_find_node_by_path("/u3@0,f8000000");
> -	if (u3 != NULL) {
> -		const u32 *vers = of_get_property(u3, "device-rev", NULL);
> -		if (vers)
> -			if (((*vers) & 0x3f) < 0x34)
> -				u3h = 0;
> -		of_node_put(u3);
> -	}
> -
> -	if (rackmac) {
> -		backside_params.G_d = BACKSIDE_PID_RACK_G_d;
> -		backside_params.input_target = BACKSIDE_PID_RACK_INPUT_TARGET;
> -		backside_params.output_min = BACKSIDE_PID_U3H_OUTPUT_MIN;
> -		backside_params.interval = BACKSIDE_PID_RACK_INTERVAL;
> -		backside_params.G_p = BACKSIDE_PID_RACK_G_p;
> -		backside_params.G_r = BACKSIDE_PID_G_r;
> -		backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
> -		backside_params.additive = 0;
> -	} else if (u3h) {
> -		backside_params.G_d = BACKSIDE_PID_U3H_G_d;
> -		backside_params.input_target = BACKSIDE_PID_U3H_INPUT_TARGET;
> -		backside_params.output_min = BACKSIDE_PID_U3H_OUTPUT_MIN;
> -		backside_params.interval = BACKSIDE_PID_INTERVAL;
> -		backside_params.G_p = BACKSIDE_PID_G_p;
> -		backside_params.G_r = BACKSIDE_PID_G_r;
> -		backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
> -		backside_params.additive = 1;
> -	} else {
> -		backside_params.G_d = BACKSIDE_PID_U3_G_d;
> -		backside_params.input_target = BACKSIDE_PID_U3_INPUT_TARGET;
> -		backside_params.output_min = BACKSIDE_PID_U3_OUTPUT_MIN;
> -		backside_params.interval = BACKSIDE_PID_INTERVAL;
> -		backside_params.G_p = BACKSIDE_PID_G_p;
> -		backside_params.G_r = BACKSIDE_PID_G_r;
> -		backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
> -		backside_params.additive = 1;
> -	}
> -
> -	state->ticks = 1;
> -	state->first = 1;
> -	state->pwm = 50;
> -
> -	state->monitor = attach_i2c_chip(BACKSIDE_MAX_ID, "backside_temp");
> -	if (state->monitor == NULL)
> -		return -ENODEV;
> -
> -	err = device_create_file(&of_dev->dev, &dev_attr_backside_temperature);
> -	err |= device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
> -	if (err)
> -		printk(KERN_WARNING "Failed to create attribute file(s)"
> -			" for backside fan\n");
> -
> -	return 0;
> -}
> -
> -/*
> - * Dispose of the state data for the backside control loop
> - */
> -static void dispose_backside_state(struct backside_pid_state *state)
> -{
> -	if (state->monitor == NULL)
> -		return;
> -
> -	device_remove_file(&of_dev->dev, &dev_attr_backside_temperature);
> -	device_remove_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
> -
> -	state->monitor = NULL;
> -}
> - 
> -/*
> - * Drives bay fan control loop
> - */
> -static void do_monitor_drives(struct drives_pid_state *state)
> -{
> -	s32 temp, integral, derivative;
> -	s64 integ_p, deriv_p, prop_p, sum; 
> -	int i, rc;
> -
> -	if (--state->ticks != 0)
> -		return;
> -	state->ticks = DRIVES_PID_INTERVAL;
> -
> -	DBG("drives:\n");
> -
> -	/* Check fan status */
> -	rc = get_rpm_fan(DRIVES_FAN_RPM_INDEX, !RPM_PID_USE_ACTUAL_SPEED);
> -	if (rc < 0) {
> -		printk(KERN_WARNING "Error %d reading drives fan !\n", rc);
> -		/* XXX What do we do now ? */
> -	} else
> -		state->rpm = rc;
> -	DBG("  current rpm: %d\n", state->rpm);
> -
> -	/* Get some sensor readings */
> -	temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor,
> -						    DS1775_TEMP)) << 8;
> -	state->last_temp = temp;
> -	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
> -	    FIX32TOPRINT(DRIVES_PID_INPUT_TARGET));
> -
> -	/* Store temperature and error in history array */
> -	state->cur_sample = (state->cur_sample + 1) % DRIVES_PID_HISTORY_SIZE;
> -	state->sample_history[state->cur_sample] = temp;
> -	state->error_history[state->cur_sample] = temp - DRIVES_PID_INPUT_TARGET;
> -	
> -	/* If first loop, fill the history table */
> -	if (state->first) {
> -		for (i = 0; i < (DRIVES_PID_HISTORY_SIZE - 1); i++) {
> -			state->cur_sample = (state->cur_sample + 1) %
> -				DRIVES_PID_HISTORY_SIZE;
> -			state->sample_history[state->cur_sample] = temp;
> -			state->error_history[state->cur_sample] =
> -				temp - DRIVES_PID_INPUT_TARGET;
> -		}
> -		state->first = 0;
> -	}
> -
> -	/* Calculate the integral term */
> -	sum = 0;
> -	integral = 0;
> -	for (i = 0; i < DRIVES_PID_HISTORY_SIZE; i++)
> -		integral += state->error_history[i];
> -	integral *= DRIVES_PID_INTERVAL;
> -	DBG("  integral: %08x\n", integral);
> -	integ_p = ((s64)DRIVES_PID_G_r) * (s64)integral;
> -	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
> -	sum += integ_p;
> -
> -	/* Calculate the derivative term */
> -	derivative = state->error_history[state->cur_sample] -
> -		state->error_history[(state->cur_sample + DRIVES_PID_HISTORY_SIZE - 1)
> -				    % DRIVES_PID_HISTORY_SIZE];
> -	derivative /= DRIVES_PID_INTERVAL;
> -	deriv_p = ((s64)DRIVES_PID_G_d) * (s64)derivative;
> -	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
> -	sum += deriv_p;
> -
> -	/* Calculate the proportional term */
> -	prop_p = ((s64)DRIVES_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
> -	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
> -	sum += prop_p;
> -
> -	/* Scale sum */
> -	sum >>= 36;
> -
> -	DBG("   sum: %d\n", (int)sum);
> -	state->rpm += (s32)sum;
> -
> -	state->rpm = max(state->rpm, DRIVES_PID_OUTPUT_MIN);
> -	state->rpm = min(state->rpm, DRIVES_PID_OUTPUT_MAX);
> -
> -	DBG("** DRIVES RPM: %d\n", (int)state->rpm);
> -	set_rpm_fan(DRIVES_FAN_RPM_INDEX, state->rpm);
> -}
> -
> -/*
> - * Initialize the state structure for the drives bay fan control loop
> - */
> -static int init_drives_state(struct drives_pid_state *state)
> -{
> -	int err;
> -
> -	state->ticks = 1;
> -	state->first = 1;
> -	state->rpm = 1000;
> -
> -	state->monitor = attach_i2c_chip(DRIVES_DALLAS_ID, "drives_temp");
> -	if (state->monitor == NULL)
> -		return -ENODEV;
> -
> -	err = device_create_file(&of_dev->dev, &dev_attr_drives_temperature);
> -	err |= device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
> -	if (err)
> -		printk(KERN_WARNING "Failed to create attribute file(s)"
> -			" for drives bay fan\n");
> -
> -	return 0;
> -}
> -
> -/*
> - * Dispose of the state data for the drives control loop
> - */
> -static void dispose_drives_state(struct drives_pid_state *state)
> -{
> -	if (state->monitor == NULL)
> -		return;
> -
> -	device_remove_file(&of_dev->dev, &dev_attr_drives_temperature);
> -	device_remove_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
> -
> -	state->monitor = NULL;
> -}
> -
> -/*
> - * DIMMs temp control loop
> - */
> -static void do_monitor_dimms(struct dimm_pid_state *state)
> -{
> -	s32 temp, integral, derivative, fan_min;
> -	s64 integ_p, deriv_p, prop_p, sum;
> -	int i;
> -
> -	if (--state->ticks != 0)
> -		return;
> -	state->ticks = DIMM_PID_INTERVAL;
> -
> -	DBG("DIMM:\n");
> -
> -	DBG("  current value: %d\n", state->output);
> -
> -	temp = read_lm87_reg(state->monitor, LM87_INT_TEMP);
> -	if (temp < 0)
> -		return;
> -	temp <<= 16;
> -	state->last_temp = temp;
> -	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
> -	    FIX32TOPRINT(DIMM_PID_INPUT_TARGET));
> -
> -	/* Store temperature and error in history array */
> -	state->cur_sample = (state->cur_sample + 1) % DIMM_PID_HISTORY_SIZE;
> -	state->sample_history[state->cur_sample] = temp;
> -	state->error_history[state->cur_sample] = temp - DIMM_PID_INPUT_TARGET;
> -
> -	/* If first loop, fill the history table */
> -	if (state->first) {
> -		for (i = 0; i < (DIMM_PID_HISTORY_SIZE - 1); i++) {
> -			state->cur_sample = (state->cur_sample + 1) %
> -				DIMM_PID_HISTORY_SIZE;
> -			state->sample_history[state->cur_sample] = temp;
> -			state->error_history[state->cur_sample] =
> -				temp - DIMM_PID_INPUT_TARGET;
> -		}
> -		state->first = 0;
> -	}
> -
> -	/* Calculate the integral term */
> -	sum = 0;
> -	integral = 0;
> -	for (i = 0; i < DIMM_PID_HISTORY_SIZE; i++)
> -		integral += state->error_history[i];
> -	integral *= DIMM_PID_INTERVAL;
> -	DBG("  integral: %08x\n", integral);
> -	integ_p = ((s64)DIMM_PID_G_r) * (s64)integral;
> -	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
> -	sum += integ_p;
> -
> -	/* Calculate the derivative term */
> -	derivative = state->error_history[state->cur_sample] -
> -		state->error_history[(state->cur_sample + DIMM_PID_HISTORY_SIZE - 1)
> -				    % DIMM_PID_HISTORY_SIZE];
> -	derivative /= DIMM_PID_INTERVAL;
> -	deriv_p = ((s64)DIMM_PID_G_d) * (s64)derivative;
> -	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
> -	sum += deriv_p;
> -
> -	/* Calculate the proportional term */
> -	prop_p = ((s64)DIMM_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
> -	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
> -	sum += prop_p;
> -
> -	/* Scale sum */
> -	sum >>= 36;
> -
> -	DBG("   sum: %d\n", (int)sum);
> -	state->output = (s32)sum;
> -	state->output = max(state->output, DIMM_PID_OUTPUT_MIN);
> -	state->output = min(state->output, DIMM_PID_OUTPUT_MAX);
> -	dimm_output_clamp = state->output;
> -
> -	DBG("** DIMM clamp value: %d\n", (int)state->output);
> -
> -	/* Backside PID is only every 5 seconds, force backside fan clamping now */
> -	fan_min = (dimm_output_clamp * 100) / 14000;
> -	fan_min = max(fan_min, backside_params.output_min);
> -	if (backside_state.pwm < fan_min) {
> -		backside_state.pwm = fan_min;
> -		DBG(" -> applying clamp to backside fan now: %d  !\n", fan_min);
> -		set_pwm_fan(BACKSIDE_FAN_PWM_INDEX, fan_min);
> -	}
> -}
> -
> -/*
> - * Initialize the state structure for the DIMM temp control loop
> - */
> -static int init_dimms_state(struct dimm_pid_state *state)
> -{
> -	state->ticks = 1;
> -	state->first = 1;
> -	state->output = 4000;
> -
> -	state->monitor = attach_i2c_chip(XSERVE_DIMMS_LM87, "dimms_temp");
> -	if (state->monitor == NULL)
> -		return -ENODEV;
> -
> -	if (device_create_file(&of_dev->dev, &dev_attr_dimms_temperature))
> -		printk(KERN_WARNING "Failed to create attribute file"
> -			" for DIMM temperature\n");
> -
> -	return 0;
> -}
> -
> -/*
> - * Dispose of the state data for the DIMM control loop
> - */
> -static void dispose_dimms_state(struct dimm_pid_state *state)
> -{
> -	if (state->monitor == NULL)
> -		return;
> -
> -	device_remove_file(&of_dev->dev, &dev_attr_dimms_temperature);
> -
> -	state->monitor = NULL;
> -}
> -
> -/*
> - * Slots fan control loop
> - */
> -static void do_monitor_slots(struct slots_pid_state *state)
> -{
> -	s32 temp, integral, derivative;
> -	s64 integ_p, deriv_p, prop_p, sum;
> -	int i, rc;
> -
> -	if (--state->ticks != 0)
> -		return;
> -	state->ticks = SLOTS_PID_INTERVAL;
> -
> -	DBG("slots:\n");
> -
> -	/* Check fan status */
> -	rc = get_pwm_fan(SLOTS_FAN_PWM_INDEX);
> -	if (rc < 0) {
> -		printk(KERN_WARNING "Error %d reading slots fan !\n", rc);
> -		/* XXX What do we do now ? */
> -	} else
> -		state->pwm = rc;
> -	DBG("  current pwm: %d\n", state->pwm);
> -
> -	/* Get some sensor readings */
> -	temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor,
> -						    DS1775_TEMP)) << 8;
> -	state->last_temp = temp;
> -	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
> -	    FIX32TOPRINT(SLOTS_PID_INPUT_TARGET));
> -
> -	/* Store temperature and error in history array */
> -	state->cur_sample = (state->cur_sample + 1) % SLOTS_PID_HISTORY_SIZE;
> -	state->sample_history[state->cur_sample] = temp;
> -	state->error_history[state->cur_sample] = temp - SLOTS_PID_INPUT_TARGET;
> -
> -	/* If first loop, fill the history table */
> -	if (state->first) {
> -		for (i = 0; i < (SLOTS_PID_HISTORY_SIZE - 1); i++) {
> -			state->cur_sample = (state->cur_sample + 1) %
> -				SLOTS_PID_HISTORY_SIZE;
> -			state->sample_history[state->cur_sample] = temp;
> -			state->error_history[state->cur_sample] =
> -				temp - SLOTS_PID_INPUT_TARGET;
> -		}
> -		state->first = 0;
> -	}
> -
> -	/* Calculate the integral term */
> -	sum = 0;
> -	integral = 0;
> -	for (i = 0; i < SLOTS_PID_HISTORY_SIZE; i++)
> -		integral += state->error_history[i];
> -	integral *= SLOTS_PID_INTERVAL;
> -	DBG("  integral: %08x\n", integral);
> -	integ_p = ((s64)SLOTS_PID_G_r) * (s64)integral;
> -	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
> -	sum += integ_p;
> -
> -	/* Calculate the derivative term */
> -	derivative = state->error_history[state->cur_sample] -
> -		state->error_history[(state->cur_sample + SLOTS_PID_HISTORY_SIZE - 1)
> -				    % SLOTS_PID_HISTORY_SIZE];
> -	derivative /= SLOTS_PID_INTERVAL;
> -	deriv_p = ((s64)SLOTS_PID_G_d) * (s64)derivative;
> -	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
> -	sum += deriv_p;
> -
> -	/* Calculate the proportional term */
> -	prop_p = ((s64)SLOTS_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
> -	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
> -	sum += prop_p;
> -
> -	/* Scale sum */
> -	sum >>= 36;
> -
> -	DBG("   sum: %d\n", (int)sum);
> -	state->pwm = (s32)sum;
> -
> -	state->pwm = max(state->pwm, SLOTS_PID_OUTPUT_MIN);
> -	state->pwm = min(state->pwm, SLOTS_PID_OUTPUT_MAX);
> -
> -	DBG("** DRIVES PWM: %d\n", (int)state->pwm);
> -	set_pwm_fan(SLOTS_FAN_PWM_INDEX, state->pwm);
> -}
> -
> -/*
> - * Initialize the state structure for the slots bay fan control loop
> - */
> -static int init_slots_state(struct slots_pid_state *state)
> -{
> -	int err;
> -
> -	state->ticks = 1;
> -	state->first = 1;
> -	state->pwm = 50;
> -
> -	state->monitor = attach_i2c_chip(XSERVE_SLOTS_LM75, "slots_temp");
> -	if (state->monitor == NULL)
> -		return -ENODEV;
> -
> -	err = device_create_file(&of_dev->dev, &dev_attr_slots_temperature);
> -	err |= device_create_file(&of_dev->dev, &dev_attr_slots_fan_pwm);
> -	if (err)
> -		printk(KERN_WARNING "Failed to create attribute file(s)"
> -			" for slots bay fan\n");
> -
> -	return 0;
> -}
> -
> -/*
> - * Dispose of the state data for the slots control loop
> - */
> -static void dispose_slots_state(struct slots_pid_state *state)
> -{
> -	if (state->monitor == NULL)
> -		return;
> -
> -	device_remove_file(&of_dev->dev, &dev_attr_slots_temperature);
> -	device_remove_file(&of_dev->dev, &dev_attr_slots_fan_pwm);
> -
> -	state->monitor = NULL;
> -}
> -
> -
> -static int call_critical_overtemp(void)
> -{
> -	char *argv[] = { critical_overtemp_path, NULL };
> -	static char *envp[] = { "HOME=/",
> -				"TERM=linux",
> -				"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
> -				NULL };
> -
> -	return call_usermodehelper(critical_overtemp_path,
> -				   argv, envp, UMH_WAIT_EXEC);
> -}
> -
> -
> -/*
> - * Here's the kernel thread that calls the various control loops
> - */
> -static int main_control_loop(void *x)
> -{
> -	DBG("main_control_loop started\n");
> -
> -	mutex_lock(&driver_lock);
> -
> -	if (start_fcu() < 0) {
> -		printk(KERN_ERR "kfand: failed to start FCU\n");
> -		mutex_unlock(&driver_lock);
> -		goto out;
> -	}
> -
> -	/* Set the PCI fan once for now on non-RackMac */
> -	if (!rackmac)
> -		set_pwm_fan(SLOTS_FAN_PWM_INDEX, SLOTS_FAN_DEFAULT_PWM);
> -
> -	/* Initialize ADCs */
> -	initialize_adc(&processor_state[0]);
> -	if (processor_state[1].monitor != NULL)
> -		initialize_adc(&processor_state[1]);
> -
> -	fcu_tickle_ticks = FCU_TICKLE_TICKS;
> -
> -	mutex_unlock(&driver_lock);
> -
> -	while (state == state_attached) {
> -		unsigned long elapsed, start;
> -
> -		start = jiffies;
> -
> -		mutex_lock(&driver_lock);
> -
> -		/* Tickle the FCU just in case */
> -		if (--fcu_tickle_ticks < 0) {
> -			fcu_tickle_ticks = FCU_TICKLE_TICKS;
> -			tickle_fcu();
> -		}
> -
> -		/* First, we always calculate the new DIMMs state on an Xserve */
> -		if (rackmac)
> -			do_monitor_dimms(&dimms_state);
> -
> -		/* Then, the CPUs */
> -		if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
> -			do_monitor_cpu_combined();
> -		else if (cpu_pid_type == CPU_PID_TYPE_RACKMAC) {
> -			do_monitor_cpu_rack(&processor_state[0]);
> -			if (processor_state[1].monitor != NULL)
> -				do_monitor_cpu_rack(&processor_state[1]);
> -			// better deal with UP
> -		} else {
> -			do_monitor_cpu_split(&processor_state[0]);
> -			if (processor_state[1].monitor != NULL)
> -				do_monitor_cpu_split(&processor_state[1]);
> -			// better deal with UP
> -		}
> -		/* Then, the rest */
> -		do_monitor_backside(&backside_state);
> -		if (rackmac)
> -			do_monitor_slots(&slots_state);
> -		else
> -			do_monitor_drives(&drives_state);
> -		mutex_unlock(&driver_lock);
> -
> -		if (critical_state == 1) {
> -			printk(KERN_WARNING "Temperature control detected a critical condition\n");
> -			printk(KERN_WARNING "Attempting to shut down...\n");
> -			if (call_critical_overtemp()) {
> -				printk(KERN_WARNING "Can't call %s, power off now!\n",
> -				       critical_overtemp_path);
> -				machine_power_off();
> -			}
> -		}
> -		if (critical_state > 0)
> -			critical_state++;
> -		if (critical_state > MAX_CRITICAL_STATE) {
> -			printk(KERN_WARNING "Shutdown timed out, power off now !\n");
> -			machine_power_off();
> -		}
> -
> -		// FIXME: Deal with signals
> -		elapsed = jiffies - start;
> -		if (elapsed < HZ)
> -			schedule_timeout_interruptible(HZ - elapsed);
> -	}
> -
> - out:
> -	DBG("main_control_loop ended\n");
> -
> -	ctrl_task = 0;
> -	complete_and_exit(&ctrl_complete, 0);
> -}
> -
> -/*
> - * Dispose the control loops when tearing down
> - */
> -static void dispose_control_loops(void)
> -{
> -	dispose_processor_state(&processor_state[0]);
> -	dispose_processor_state(&processor_state[1]);
> -	dispose_backside_state(&backside_state);
> -	dispose_drives_state(&drives_state);
> -	dispose_slots_state(&slots_state);
> -	dispose_dimms_state(&dimms_state);
> -}
> -
> -/*
> - * Create the control loops. U3-0 i2c bus is up, so we can now
> - * get to the various sensors
> - */
> -static int create_control_loops(void)
> -{
> -	struct device_node *np;
> -
> -	/* Count CPUs from the device-tree, we don't care how many are
> -	 * actually used by Linux
> -	 */
> -	cpu_count = 0;
> -	for (np = NULL; NULL != (np = of_find_node_by_type(np, "cpu"));)
> -		cpu_count++;
> -
> -	DBG("counted %d CPUs in the device-tree\n", cpu_count);
> -
> -	/* Decide the type of PID algorithm to use based on the presence of
> -	 * the pumps, though that may not be the best way, that is good enough
> -	 * for now
> -	 */
> -	if (rackmac)
> -		cpu_pid_type = CPU_PID_TYPE_RACKMAC;
> -	else if (of_machine_is_compatible("PowerMac7,3")
> -	    && (cpu_count > 1)
> -	    && fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID
> -	    && fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) {
> -		printk(KERN_INFO "Liquid cooling pumps detected, using new algorithm !\n");
> -		cpu_pid_type = CPU_PID_TYPE_COMBINED;
> -	} else
> -		cpu_pid_type = CPU_PID_TYPE_SPLIT;
> -
> -	/* Create control loops for everything. If any fail, everything
> -	 * fails
> -	 */
> -	if (init_processor_state(&processor_state[0], 0))
> -		goto fail;
> -	if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
> -		fetch_cpu_pumps_minmax();
> -
> -	if (cpu_count > 1 && init_processor_state(&processor_state[1], 1))
> -		goto fail;
> -	if (init_backside_state(&backside_state))
> -		goto fail;
> -	if (rackmac && init_dimms_state(&dimms_state))
> -		goto fail;
> -	if (rackmac && init_slots_state(&slots_state))
> -		goto fail;
> -	if (!rackmac && init_drives_state(&drives_state))
> -		goto fail;
> -
> -	DBG("all control loops up !\n");
> -
> -	return 0;
> -	
> - fail:
> -	DBG("failure creating control loops, disposing\n");
> -
> -	dispose_control_loops();
> -
> -	return -ENODEV;
> -}
> -
> -/*
> - * Start the control loops after everything is up, that is create
> - * the thread that will make them run
> - */
> -static void start_control_loops(void)
> -{
> -	init_completion(&ctrl_complete);
> -
> -	ctrl_task = kthread_run(main_control_loop, NULL, "kfand");
> -}
> -
> -/*
> - * Stop the control loops when tearing down
> - */
> -static void stop_control_loops(void)
> -{
> -	if (ctrl_task)
> -		wait_for_completion(&ctrl_complete);
> -}
> -
> -/*
> - * Attach to the i2c FCU after detecting U3-1 bus
> - */
> -static int attach_fcu(void)
> -{
> -	fcu = attach_i2c_chip(FAN_CTRLER_ID, "fcu");
> -	if (fcu == NULL)
> -		return -ENODEV;
> -
> -	DBG("FCU attached\n");
> -
> -	return 0;
> -}
> -
> -/*
> - * Detach from the i2c FCU when tearing down
> - */
> -static void detach_fcu(void)
> -{
> -	fcu = NULL;
> -}
> -
> -/*
> - * Attach to the i2c controller. We probe the various chips based
> - * on the device-tree nodes and build everything for the driver to
> - * run, we then kick the driver monitoring thread
> - */
> -static int therm_pm72_attach(struct i2c_adapter *adapter)
> -{
> -	mutex_lock(&driver_lock);
> -
> -	/* Check state */
> -	if (state == state_detached)
> -		state = state_attaching;
> -	if (state != state_attaching) {
> -		mutex_unlock(&driver_lock);
> -		return 0;
> -	}
> -
> -	/* Check if we are looking for one of these */
> -	if (u3_0 == NULL && !strcmp(adapter->name, "u3 0")) {
> -		u3_0 = adapter;
> -		DBG("found U3-0\n");
> -		if (k2 || !rackmac)
> -			if (create_control_loops())
> -				u3_0 = NULL;
> -	} else if (u3_1 == NULL && !strcmp(adapter->name, "u3 1")) {
> -		u3_1 = adapter;
> -		DBG("found U3-1, attaching FCU\n");
> -		if (attach_fcu())
> -			u3_1 = NULL;
> -	} else if (k2 == NULL && !strcmp(adapter->name, "mac-io 0")) {
> -		k2 = adapter;
> -		DBG("Found K2\n");
> -		if (u3_0 && rackmac)
> -			if (create_control_loops())
> -				k2 = NULL;
> -	}
> -	/* We got all we need, start control loops */
> -	if (u3_0 != NULL && u3_1 != NULL && (k2 || !rackmac)) {
> -		DBG("everything up, starting control loops\n");
> -		state = state_attached;
> -		start_control_loops();
> -	}
> -	mutex_unlock(&driver_lock);
> -
> -	return 0;
> -}
> -
> -static int therm_pm72_probe(struct i2c_client *client,
> -			    const struct i2c_device_id *id)
> -{
> -	/* Always succeed, the real work was done in therm_pm72_attach() */
> -	return 0;
> -}
> -
> -/*
> - * Called when any of the devices which participates into thermal management
> - * is going away.
> - */
> -static int therm_pm72_remove(struct i2c_client *client)
> -{
> -	struct i2c_adapter *adapter = client->adapter;
> -
> -	mutex_lock(&driver_lock);
> -
> -	if (state != state_detached)
> -		state = state_detaching;
> -
> -	/* Stop control loops if any */
> -	DBG("stopping control loops\n");
> -	mutex_unlock(&driver_lock);
> -	stop_control_loops();
> -	mutex_lock(&driver_lock);
> -
> -	if (u3_0 != NULL && !strcmp(adapter->name, "u3 0")) {
> -		DBG("lost U3-0, disposing control loops\n");
> -		dispose_control_loops();
> -		u3_0 = NULL;
> -	}
> -	
> -	if (u3_1 != NULL && !strcmp(adapter->name, "u3 1")) {
> -		DBG("lost U3-1, detaching FCU\n");
> -		detach_fcu();
> -		u3_1 = NULL;
> -	}
> -	if (u3_0 == NULL && u3_1 == NULL)
> -		state = state_detached;
> -
> -	mutex_unlock(&driver_lock);
> -
> -	return 0;
> -}
> -
> -/*
> - * i2c_driver structure to attach to the host i2c controller
> - */
> -
> -static const struct i2c_device_id therm_pm72_id[] = {
> -	/*
> -	 * Fake device name, thermal management is done by several
> -	 * chips but we don't need to differentiate between them at
> -	 * this point.
> -	 */
> -	{ "therm_pm72", 0 },
> -	{ }
> -};
> -
> -static struct i2c_driver therm_pm72_driver = {
> -	.driver = {
> -		.name	= "therm_pm72",
> -	},
> -	.attach_adapter	= therm_pm72_attach,
> -	.probe		= therm_pm72_probe,
> -	.remove		= therm_pm72_remove,
> -	.id_table	= therm_pm72_id,
> -};
> -
> -static int fan_check_loc_match(const char *loc, int fan)
> -{
> -	char	tmp[64];
> -	char	*c, *e;
> -
> -	strlcpy(tmp, fcu_fans[fan].loc, 64);
> -
> -	c = tmp;
> -	for (;;) {
> -		e = strchr(c, ',');
> -		if (e)
> -			*e = 0;
> -		if (strcmp(loc, c) == 0)
> -			return 1;
> -		if (e == NULL)
> -			break;
> -		c = e + 1;
> -	}
> -	return 0;
> -}
> -
> -static void fcu_lookup_fans(struct device_node *fcu_node)
> -{
> -	struct device_node *np = NULL;
> -	int i;
> -
> -	/* The table is filled by default with values that are suitable
> -	 * for the old machines without device-tree informations. We scan
> -	 * the device-tree and override those values with whatever is
> -	 * there
> -	 */
> -
> -	DBG("Looking up FCU controls in device-tree...\n");
> -
> -	while ((np = of_get_next_child(fcu_node, np)) != NULL) {
> -		int type = -1;
> -		const char *loc;
> -		const u32 *reg;
> -
> -		DBG(" control: %s, type: %s\n", np->name, np->type);
> -
> -		/* Detect control type */
> -		if (!strcmp(np->type, "fan-rpm-control") ||
> -		    !strcmp(np->type, "fan-rpm"))
> -			type = FCU_FAN_RPM;
> -		if (!strcmp(np->type, "fan-pwm-control") ||
> -		    !strcmp(np->type, "fan-pwm"))
> -			type = FCU_FAN_PWM;
> -		/* Only care about fans for now */
> -		if (type == -1)
> -			continue;
> -
> -		/* Lookup for a matching location */
> -		loc = of_get_property(np, "location", NULL);
> -		reg = of_get_property(np, "reg", NULL);
> -		if (loc == NULL || reg == NULL)
> -			continue;
> -		DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg);
> -
> -		for (i = 0; i < FCU_FAN_COUNT; i++) {
> -			int fan_id;
> -
> -			if (!fan_check_loc_match(loc, i))
> -				continue;
> -			DBG(" location match, index: %d\n", i);
> -			fcu_fans[i].id = FCU_FAN_ABSENT_ID;
> -			if (type != fcu_fans[i].type) {
> -				printk(KERN_WARNING "therm_pm72: Fan type mismatch "
> -				       "in device-tree for %s\n", np->full_name);
> -				break;
> -			}
> -			if (type == FCU_FAN_RPM)
> -				fan_id = ((*reg) - 0x10) / 2;
> -			else
> -				fan_id = ((*reg) - 0x30) / 2;
> -			if (fan_id > 7) {
> -				printk(KERN_WARNING "therm_pm72: Can't parse "
> -				       "fan ID in device-tree for %s\n", np->full_name);
> -				break;
> -			}
> -			DBG(" fan id -> %d, type -> %d\n", fan_id, type);
> -			fcu_fans[i].id = fan_id;
> -		}
> -	}
> -
> -	/* Now dump the array */
> -	printk(KERN_INFO "Detected fan controls:\n");
> -	for (i = 0; i < FCU_FAN_COUNT; i++) {
> -		if (fcu_fans[i].id == FCU_FAN_ABSENT_ID)
> -			continue;
> -		printk(KERN_INFO "  %d: %s fan, id %d, location: %s\n", i,
> -		       fcu_fans[i].type == FCU_FAN_RPM ? "RPM" : "PWM",
> -		       fcu_fans[i].id, fcu_fans[i].loc);
> -	}
> -}
> -
> -static int fcu_of_probe(struct platform_device* dev)
> -{
> -	state = state_detached;
> -	of_dev = dev;
> -
> -	dev_info(&dev->dev, "PowerMac G5 Thermal control driver %s\n", VERSION);
> -
> -	/* Lookup the fans in the device tree */
> -	fcu_lookup_fans(dev->dev.of_node);
> -
> -	/* Add the driver */
> -	return i2c_add_driver(&therm_pm72_driver);
> -}
> -
> -static int fcu_of_remove(struct platform_device* dev)
> -{
> -	i2c_del_driver(&therm_pm72_driver);
> -
> -	return 0;
> -}
> -
> -static const struct of_device_id fcu_match[] = 
> -{
> -	{
> -	.type		= "fcu",
> -	},
> -	{},
> -};
> -MODULE_DEVICE_TABLE(of, fcu_match);
> -
> -static struct platform_driver fcu_of_platform_driver = 
> -{
> -	.driver = {
> -		.name = "temperature",
> -		.owner = THIS_MODULE,
> -		.of_match_table = fcu_match,
> -	},
> -	.probe		= fcu_of_probe,
> -	.remove		= fcu_of_remove
> -};
> -
> -/*
> - * Check machine type, attach to i2c controller
> - */
> -static int __init therm_pm72_init(void)
> -{
> -	rackmac = of_machine_is_compatible("RackMac3,1");
> -
> -	if (!of_machine_is_compatible("PowerMac7,2") &&
> -	    !of_machine_is_compatible("PowerMac7,3") &&
> -	    !rackmac)
> -	    	return -ENODEV;
> -
> -	return platform_driver_register(&fcu_of_platform_driver);
> -}
> -
> -static void __exit therm_pm72_exit(void)
> -{
> -	platform_driver_unregister(&fcu_of_platform_driver);
> -}
> -
> -module_init(therm_pm72_init);
> -module_exit(therm_pm72_exit);
> -
> -MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
> -MODULE_DESCRIPTION("Driver for Apple's PowerMac G5 thermal control");
> -MODULE_LICENSE("GPL");
> -
> diff --git a/drivers/macintosh/therm_pm72.h b/drivers/macintosh/therm_pm72.h
> deleted file mode 100644
> index df3680e2a22f..000000000000
> --- a/drivers/macintosh/therm_pm72.h
> +++ /dev/null
> @@ -1,326 +0,0 @@
> -#ifndef __THERM_PMAC_7_2_H__
> -#define __THERM_PMAC_7_2_H__
> -
> -typedef unsigned short fu16;
> -typedef int fs32;
> -typedef short fs16;
> -
> -struct mpu_data
> -{
> -	u8	signature;		/* 0x00 - EEPROM sig. */
> -	u8	bytes_used;		/* 0x01 - Bytes used in eeprom (160 ?) */
> -	u8	size;			/* 0x02 - EEPROM size (256 ?) */
> -	u8	version;		/* 0x03 - EEPROM version */
> -	u32	data_revision;		/* 0x04 - Dataset revision */
> -	u8	processor_bin_code[3];	/* 0x08 - Processor BIN code */
> -	u8	bin_code_expansion;	/* 0x0b - ??? (padding ?) */
> -	u8	processor_num;		/* 0x0c - Number of CPUs on this MPU */
> -	u8	input_mul_bus_div;	/* 0x0d - Clock input multiplier/bus divider */
> -	u8	reserved1[2];		/* 0x0e - */
> -	u32	input_clk_freq_high;	/* 0x10 - Input clock frequency high */
> -	u8	cpu_nb_target_cycles;	/* 0x14 - ??? */
> -	u8	cpu_statlat;		/* 0x15 - ??? */
> -	u8	cpu_snooplat;		/* 0x16 - ??? */
> -	u8	cpu_snoopacc;		/* 0x17 - ??? */
> -	u8	nb_paamwin;		/* 0x18 - ??? */
> -	u8	nb_statlat;		/* 0x19 - ??? */
> -	u8	nb_snooplat;		/* 0x1a - ??? */
> -	u8	nb_snoopwin;		/* 0x1b - ??? */
> -	u8	api_bus_mode;		/* 0x1c - ??? */
> -	u8	reserved2[3];		/* 0x1d - */
> -	u32	input_clk_freq_low;	/* 0x20 - Input clock frequency low */
> -	u8	processor_card_slot;	/* 0x24 - Processor card slot number */
> -	u8	reserved3[2];		/* 0x25 - */
> -	u8	padjmax;       		/* 0x27 - Max power adjustment (Not in OF!) */
> -	u8	ttarget;		/* 0x28 - Target temperature */
> -	u8	tmax;			/* 0x29 - Max temperature */
> -	u8	pmaxh;			/* 0x2a - Max power */
> -	u8	tguardband;		/* 0x2b - Guardband temp ??? Hist. len in OSX */
> -	fs32	pid_gp;			/* 0x2c - PID proportional gain */
> -	fs32	pid_gr;			/* 0x30 - PID reset gain */
> -	fs32	pid_gd;			/* 0x34 - PID derivative gain */
> -	fu16	voph;			/* 0x38 - Vop High */
> -	fu16	vopl;			/* 0x3a - Vop Low */
> -	fs16	nactual_die;		/* 0x3c - nActual Die */
> -	fs16	nactual_heatsink;	/* 0x3e - nActual Heatsink */
> -	fs16	nactual_system;		/* 0x40 - nActual System */
> -	u16	calibration_flags;	/* 0x42 - Calibration flags */
> -	fu16	mdiode;			/* 0x44 - Diode M value (scaling factor) */
> -	fs16	bdiode;			/* 0x46 - Diode B value (offset) */
> -	fs32	theta_heat_sink;	/* 0x48 - Theta heat sink */
> -	u16	rminn_intake_fan;	/* 0x4c - Intake fan min RPM */
> -	u16	rmaxn_intake_fan;	/* 0x4e - Intake fan max RPM */
> -	u16	rminn_exhaust_fan;	/* 0x50 - Exhaust fan min RPM */
> -	u16	rmaxn_exhaust_fan;	/* 0x52 - Exhaust fan max RPM */
> -	u8	processor_part_num[8];	/* 0x54 - Processor part number XX pumps min/max */
> -	u32	processor_lot_num;	/* 0x5c - Processor lot number */
> -	u8	orig_card_sernum[0x10];	/* 0x60 - Card original serial number */
> -	u8	curr_card_sernum[0x10];	/* 0x70 - Card current serial number */
> -	u8	mlb_sernum[0x18];	/* 0x80 - MLB serial number */
> -	u32	checksum1;		/* 0x98 - */
> -	u32	checksum2;		/* 0x9c - */	
> -}; /* Total size = 0xa0 */
> -
> -/* Display a 16.16 fixed point value */
> -#define FIX32TOPRINT(f)	((f) >> 16),((((f) & 0xffff) * 1000) >> 16)
> -
> -/*
> - * Maximum number of seconds to be in critical state (after a
> - * normal shutdown attempt). If the machine isn't down after
> - * this counter elapses, we force an immediate machine power
> - * off.
> - */
> -#define MAX_CRITICAL_STATE			30
> -static char * critical_overtemp_path = "/sbin/critical_overtemp";
> -
> -/*
> - * This option is "weird" :) Basically, if you define this to 1
> - * the control loop for the RPMs fans (not PWMs) will apply the
> - * correction factor obtained from the PID to the _actual_ RPM
> - * speed read from the FCU.
> - * If you define the below constant to 0, then it will be
> - * applied to the setpoint RPM speed, that is basically the
> - * speed we proviously "asked" for.
> - *
> - * I'm not sure which of these Apple's algorithm is supposed
> - * to use
> - */
> -#define RPM_PID_USE_ACTUAL_SPEED		0
> -
> -/*
> - * i2c IDs. Currently, we hard code those and assume that
> - * the FCU is on U3 bus 1 while all sensors are on U3 bus
> - * 0. This appear to be safe enough for this first version
> - * of the driver, though I would accept any clean patch
> - * doing a better use of the device-tree without turning the
> - * while i2c registration mechanism into a racy mess
> - *
> - * Note: Xserve changed this. We have some bits on the K2 bus,
> - * which I arbitrarily set to 0x200. Ultimately, we really want
> - * too lookup these in the device-tree though
> - */
> -#define FAN_CTRLER_ID		0x15e
> -#define SUPPLY_MONITOR_ID      	0x58
> -#define SUPPLY_MONITORB_ID     	0x5a
> -#define DRIVES_DALLAS_ID	0x94
> -#define BACKSIDE_MAX_ID		0x98
> -#define XSERVE_DIMMS_LM87	0x25a
> -#define XSERVE_SLOTS_LM75	0x290
> -
> -/*
> - * Some MAX6690, DS1775, LM87 register definitions
> - */
> -#define MAX6690_INT_TEMP	0
> -#define MAX6690_EXT_TEMP	1
> -#define DS1775_TEMP		0
> -#define LM87_INT_TEMP		0x27
> -
> -/*
> - * Scaling factors for the AD7417 ADC converters (except
> - * for the CPU diode which is obtained from the EEPROM).
> - * Those values are obtained from the property list of
> - * the darwin driver
> - */
> -#define ADC_12V_CURRENT_SCALE	0x0320	/* _AD2 */
> -#define ADC_CPU_VOLTAGE_SCALE	0x00a0	/* _AD3 */
> -#define ADC_CPU_CURRENT_SCALE	0x1f40	/* _AD4 */
> -
> -/*
> - * PID factors for the U3/Backside fan control loop. We have 2 sets
> - * of values here, one set for U3 and one set for U3H
> - */
> -#define BACKSIDE_FAN_PWM_DEFAULT_ID	1
> -#define BACKSIDE_FAN_PWM_INDEX		0
> -#define BACKSIDE_PID_U3_G_d		0x02800000
> -#define BACKSIDE_PID_U3H_G_d		0x01400000
> -#define BACKSIDE_PID_RACK_G_d		0x00500000
> -#define BACKSIDE_PID_G_p		0x00500000
> -#define BACKSIDE_PID_RACK_G_p		0x0004cccc
> -#define BACKSIDE_PID_G_r		0x00000000
> -#define BACKSIDE_PID_U3_INPUT_TARGET	0x00410000
> -#define BACKSIDE_PID_U3H_INPUT_TARGET	0x004b0000
> -#define BACKSIDE_PID_RACK_INPUT_TARGET	0x00460000
> -#define BACKSIDE_PID_INTERVAL		5
> -#define BACKSIDE_PID_RACK_INTERVAL	1
> -#define BACKSIDE_PID_OUTPUT_MAX		100
> -#define BACKSIDE_PID_U3_OUTPUT_MIN	20
> -#define BACKSIDE_PID_U3H_OUTPUT_MIN	20
> -#define BACKSIDE_PID_HISTORY_SIZE	2
> -
> -struct basckside_pid_params
> -{
> -	s32			G_d;
> -	s32			G_p;
> -	s32			G_r;
> -	s32			input_target;
> -	s32			output_min;
> -	s32			output_max;
> -	s32			interval;
> -	int			additive;
> -};
> -
> -struct backside_pid_state
> -{
> -	int			ticks;
> -	struct i2c_client *	monitor;
> -	s32		       	sample_history[BACKSIDE_PID_HISTORY_SIZE];
> -	s32			error_history[BACKSIDE_PID_HISTORY_SIZE];
> -	int			cur_sample;
> -	s32			last_temp;
> -	int			pwm;
> -	int			first;
> -};
> -
> -/*
> - * PID factors for the Drive Bay fan control loop
> - */
> -#define DRIVES_FAN_RPM_DEFAULT_ID	2
> -#define DRIVES_FAN_RPM_INDEX		1
> -#define DRIVES_PID_G_d			0x01e00000
> -#define DRIVES_PID_G_p			0x00500000
> -#define DRIVES_PID_G_r			0x00000000
> -#define DRIVES_PID_INPUT_TARGET		0x00280000
> -#define DRIVES_PID_INTERVAL    		5
> -#define DRIVES_PID_OUTPUT_MAX		4000
> -#define DRIVES_PID_OUTPUT_MIN		300
> -#define DRIVES_PID_HISTORY_SIZE		2
> -
> -struct drives_pid_state
> -{
> -	int			ticks;
> -	struct i2c_client *	monitor;
> -	s32	       		sample_history[BACKSIDE_PID_HISTORY_SIZE];
> -	s32			error_history[BACKSIDE_PID_HISTORY_SIZE];
> -	int			cur_sample;
> -	s32			last_temp;
> -	int			rpm;
> -	int			first;
> -};
> -
> -#define SLOTS_FAN_PWM_DEFAULT_ID	2
> -#define SLOTS_FAN_PWM_INDEX		2
> -#define	SLOTS_FAN_DEFAULT_PWM		40 /* Do better here ! */
> -
> -
> -/*
> - * PID factors for the Xserve DIMM control loop
> - */
> -#define DIMM_PID_G_d			0
> -#define DIMM_PID_G_p			0
> -#define DIMM_PID_G_r			0x06553600
> -#define DIMM_PID_INPUT_TARGET		3276800
> -#define DIMM_PID_INTERVAL    		1
> -#define DIMM_PID_OUTPUT_MAX		14000
> -#define DIMM_PID_OUTPUT_MIN		4000
> -#define DIMM_PID_HISTORY_SIZE		20
> -
> -struct dimm_pid_state
> -{
> -	int			ticks;
> -	struct i2c_client *	monitor;
> -	s32	       		sample_history[DIMM_PID_HISTORY_SIZE];
> -	s32			error_history[DIMM_PID_HISTORY_SIZE];
> -	int			cur_sample;
> -	s32			last_temp;
> -	int			first;
> -	int			output;
> -};
> -
> -
> -/*
> - * PID factors for the Xserve Slots control loop
> - */
> -#define SLOTS_PID_G_d			0
> -#define SLOTS_PID_G_p			0
> -#define SLOTS_PID_G_r			0x00100000
> -#define SLOTS_PID_INPUT_TARGET		3200000
> -#define SLOTS_PID_INTERVAL    		1
> -#define SLOTS_PID_OUTPUT_MAX		100
> -#define SLOTS_PID_OUTPUT_MIN		20
> -#define SLOTS_PID_HISTORY_SIZE		20
> -
> -struct slots_pid_state
> -{
> -	int			ticks;
> -	struct i2c_client *	monitor;
> -	s32	       		sample_history[SLOTS_PID_HISTORY_SIZE];
> -	s32			error_history[SLOTS_PID_HISTORY_SIZE];
> -	int			cur_sample;
> -	s32			last_temp;
> -	int			first;
> -	int			pwm;
> -};
> -
> -
> -
> -/* Desktops */
> -
> -#define CPUA_INTAKE_FAN_RPM_DEFAULT_ID	3
> -#define CPUA_EXHAUST_FAN_RPM_DEFAULT_ID	4
> -#define CPUB_INTAKE_FAN_RPM_DEFAULT_ID	5
> -#define CPUB_EXHAUST_FAN_RPM_DEFAULT_ID	6
> -
> -#define CPUA_INTAKE_FAN_RPM_INDEX	3
> -#define CPUA_EXHAUST_FAN_RPM_INDEX	4
> -#define CPUB_INTAKE_FAN_RPM_INDEX	5
> -#define CPUB_EXHAUST_FAN_RPM_INDEX	6
> -
> -#define CPU_INTAKE_SCALE		0x0000f852
> -#define CPU_TEMP_HISTORY_SIZE		2
> -#define CPU_POWER_HISTORY_SIZE		10
> -#define CPU_PID_INTERVAL		1
> -#define CPU_MAX_OVERTEMP		90
> -
> -#define CPUA_PUMP_RPM_INDEX		7
> -#define CPUB_PUMP_RPM_INDEX		8
> -#define CPU_PUMP_OUTPUT_MAX		3200
> -#define CPU_PUMP_OUTPUT_MIN		1250
> -
> -/* Xserve */
> -#define CPU_A1_FAN_RPM_INDEX		9
> -#define CPU_A2_FAN_RPM_INDEX		10
> -#define CPU_A3_FAN_RPM_INDEX		11
> -#define CPU_B1_FAN_RPM_INDEX		12
> -#define CPU_B2_FAN_RPM_INDEX		13
> -#define CPU_B3_FAN_RPM_INDEX		14
> -
> -
> -struct cpu_pid_state
> -{
> -	int			index;
> -	struct i2c_client *	monitor;
> -	struct mpu_data		mpu;
> -	int			overtemp;
> -	s32	       		temp_history[CPU_TEMP_HISTORY_SIZE];
> -	int			cur_temp;
> -	s32			power_history[CPU_POWER_HISTORY_SIZE];
> -	s32			error_history[CPU_POWER_HISTORY_SIZE];
> -	int			cur_power;
> -	int			count_power;
> -	int			rpm;
> -	int			intake_rpm;
> -	s32			voltage;
> -	s32			current_a;
> -	s32			last_temp;
> -	s32			last_power;
> -	int			first;
> -	u8			adc_config;
> -	s32			pump_min;
> -	s32			pump_max;
> -};
> -
> -/* Tickle FCU every 10 seconds */
> -#define FCU_TICKLE_TICKS	10
> -
> -/*
> - * Driver state
> - */
> -enum {
> -	state_detached,
> -	state_attaching,
> -	state_attached,
> -	state_detaching,
> -};
> -
> -
> -#endif /* __THERM_PMAC_7_2_H__ */

^ permalink raw reply

* [PATCH 1/2] powerpc/powernv: Add OPAL IPMI interface
From: Jeremy Kerr @ 2014-11-06  3:38 UTC (permalink / raw)
  To: openipmi-developer, Corey Minyard, linuxppc-dev
In-Reply-To: <1415245107.292153.444216242273.0.gpush@pablo>

Recent OPAL firmare adds a couple of functions to send and receive IPMI
messages:

  https://github.com/open-power/skiboot/commit/b2a374da

This change updates the token list and wrappers to suit, and adds the
platform devices for any IPMI interfaces.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>

---
 arch/powerpc/include/asm/opal.h                |   17 +++++++++++++++++
 arch/powerpc/platforms/powernv/opal-wrappers.S |    2 ++
 arch/powerpc/platforms/powernv/opal.c          |   14 ++++++++++++++
 3 files changed, 33 insertions(+)

diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 9124b0e..5d073e5 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -154,6 +154,8 @@ struct opal_sg_list {
 #define OPAL_HANDLE_HMI				98
 #define OPAL_REGISTER_DUMP_REGION		101
 #define OPAL_UNREGISTER_DUMP_REGION		102
+#define OPAL_IPMI_SEND				107
+#define OPAL_IPMI_RECV				108
 
 #ifndef __ASSEMBLY__
 
@@ -452,6 +454,17 @@ struct opal_msg {
 	__be64 params[8];
 };
 
+enum {
+	OPAL_IPMI_MSG_FORMAT_VERSION_1 = 1,
+};
+
+struct opal_ipmi_msg {
+	uint8_t		version;
+	uint8_t		netfn;
+	uint8_t		cmd;
+	uint8_t		data[];
+};
+
 struct opal_machine_check_event {
 	enum OpalMCE_Version	version:8;	/* 0x00 */
 	uint8_t			in_use;		/* 0x01 */
@@ -963,6 +976,10 @@ int64_t opal_handle_hmi(void);
 int64_t opal_register_dump_region(uint32_t id, uint64_t start, uint64_t end);
 int64_t opal_unregister_dump_region(uint32_t id);
 int64_t opal_pci_set_phb_cxl_mode(uint64_t phb_id, uint64_t mode, uint64_t pe_number);
+int64_t opal_ipmi_send(uint64_t interface, struct opal_ipmi_msg *msg,
+		uint64_t msg_len);
+int64_t opal_ipmi_recv(uint64_t interface, struct opal_ipmi_msg *msg,
+		uint64_t *msg_len);
 
 /* Internal functions */
 extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index e9e2450..f463068 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -250,3 +250,5 @@ OPAL_CALL(opal_handle_hmi,			OPAL_HANDLE_HMI);
 OPAL_CALL(opal_register_dump_region,		OPAL_REGISTER_DUMP_REGION);
 OPAL_CALL(opal_unregister_dump_region,		OPAL_UNREGISTER_DUMP_REGION);
 OPAL_CALL(opal_pci_set_phb_cxl_mode,		OPAL_PCI_SET_PHB_CXL_MODE);
+OPAL_CALL(opal_ipmi_send,			OPAL_IPMI_SEND);
+OPAL_CALL(opal_ipmi_recv,			OPAL_IPMI_RECV);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index b642b05..c2f873e 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -623,6 +623,16 @@ static void __init opal_dump_region_init(void)
 		pr_warn("DUMP: Failed to register kernel log buffer. "
 			"rc = %d\n", rc);
 }
+
+static void opal_ipmi_init(struct device_node *opal_node)
+{
+	struct device_node *np;
+
+	for_each_child_of_node(opal_node, np)
+		if (of_device_is_compatible(np, "ibm,opal-ipmi"))
+			of_platform_device_create(np, NULL, NULL);
+}
+
 static int __init opal_init(void)
 {
 	struct device_node *np, *consoles;
@@ -686,6 +696,8 @@ static int __init opal_init(void)
 		opal_msglog_init();
 	}
 
+	opal_ipmi_init(opal_node);
+
 	return 0;
 }
 machine_subsys_initcall(powernv, opal_init);
@@ -721,6 +733,8 @@ void opal_shutdown(void)
 
 /* Export this so that test modules can use it */
 EXPORT_SYMBOL_GPL(opal_invalid_call);
+EXPORT_SYMBOL_GPL(opal_ipmi_send);
+EXPORT_SYMBOL_GPL(opal_ipmi_recv);
 
 /* Convert a region of vmalloc memory to an opal sg list */
 struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,

^ permalink raw reply related

* [PATCH 0/2] Add IPMI support for powernv powerpc machines
From: Jeremy Kerr @ 2014-11-06  3:38 UTC (permalink / raw)
  To: openipmi-developer, Corey Minyard, linuxppc-dev
In-Reply-To: <1415245107.292153.444216242273.0.gpush@pablo>

This series adds IPMI driver and arch glue for OPAL-firmware-based
powernv machines. The first change exposes the firmware's IPMI API, and
the second adds an actual driver.

IPMI folks: the IPMI driver could do with a little review, as it's not a
conventional BT/KCS/SMI SI, in that the low-level send/recv interface
will handle the entire message at once.

Corey & Michael: if this is acceptable, it may be mergable as two
separate patches - one for the IPMI subsystem, one for the powernv
platform.  However, we'd need to preserve their order: patch 2/2 depends
on 1/2, which provides the structure & function definitions. This'll
break the build if only 2/2 is in the tree, and CONFIG_IPMI_POWERNV is
set.

Alternatively, they could be merged by one maintainer, pending an ack
from the other.

Comments / queries / etc welcome.

Cheers,


Jeremy

---
Jeremy Kerr (2):
      powerpc/powernv: Add OPAL IPMI interface
      drivers/char/ipmi: Add powernv IPMI driver

^ permalink raw reply

* [PATCH 2/2] drivers/char/ipmi: Add powernv IPMI driver
From: Jeremy Kerr @ 2014-11-06  3:38 UTC (permalink / raw)
  To: openipmi-developer, Corey Minyard, linuxppc-dev
In-Reply-To: <1415245107.292153.444216242273.0.gpush@pablo>

This change adds an initial IPMI driver for powerpc OPAL firmware. The
interface is exposed entirely through firmware: we have two functions to
send and receive IPMI messages, and an interrupt notification from the
firmware to signify that a message is available.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>

---
 drivers/char/ipmi/Kconfig        |    6 
 drivers/char/ipmi/Makefile       |    1 
 drivers/char/ipmi/ipmi_powernv.c |  298 +++++++++++++++++++++++++++++++
 3 files changed, 305 insertions(+)

diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index db1c9b7..a6a8cb3 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -62,6 +62,12 @@ config IPMI_SI_PROBE_DEFAULTS
 	 only be available on older systems if the "ipmi_si_intf.trydefaults=1"
 	 boot argument is passed.
 
+config IPMI_POWERNV
+       depends on PPC_POWERNV
+       tristate 'POWERNV (OPAL firmware) IPMI interface'
+       help
+         Provides a driver for OPAL firmware-based IPMI interfaces.
+
 config IPMI_WATCHDOG
        tristate 'IPMI Watchdog Timer'
        help
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 16a9364..6620404 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -7,5 +7,6 @@ ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o
 obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
 obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
 obj-$(CONFIG_IPMI_SI) += ipmi_si.o
+obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
 obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
 obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c
new file mode 100644
index 0000000..79bbed9
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_powernv.c
@@ -0,0 +1,298 @@
+
+#define pr_fmt(fmt)        "ipmi-powernv: " fmt
+
+#include <linux/ipmi_smi.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <asm/opal.h>
+
+
+struct ipmi_smi_powernv {
+	u64			interface_id;
+	struct ipmi_device_id	ipmi_id;
+	ipmi_smi_t		intf;
+	u64			event;
+	struct notifier_block	event_nb;
+
+	/**
+	 * We assume that there can only be one outstanding request, so
+	 * keep the pending message in cur_msg. We protect this from concurrent
+	 * updates through send & recv calls, (and consequently opal_msg, which
+	 * is in-use when cur_msg is set) with msg_lock
+	 */
+	spinlock_t		msg_lock;
+	struct ipmi_smi_msg	*cur_msg;
+	struct opal_ipmi_msg	*opal_msg;
+};
+
+static int ipmi_powernv_start_processing(void *send_info, ipmi_smi_t intf)
+{
+	struct ipmi_smi_powernv *smi = send_info;
+	smi->intf = intf;
+	return 0;
+}
+
+static void send_error_reply(struct ipmi_smi_powernv *smi,
+		struct ipmi_smi_msg *msg, u8 completion_code)
+{
+	msg->rsp[0] = msg->data[0] | 0x4;
+	msg->rsp[1] = msg->data[1];
+	msg->rsp[2] = completion_code;
+	msg->rsp_size = 3;
+	ipmi_smi_msg_received(smi->intf, msg);
+}
+
+static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg,
+		int priority)
+{
+	struct ipmi_smi_powernv *smi = send_info;
+	struct opal_ipmi_msg *opal_msg;
+	unsigned long flags;
+	int comp, rc;
+	size_t size;
+
+	/* ensure data_len will fit in the opal_ipmi_msg buffer... */
+	if (msg->data_size > IPMI_MAX_MSG_LENGTH) {
+		comp = IPMI_REQ_LEN_EXCEEDED_ERR;
+		goto err;
+	}
+
+	/* ... and that we at least have netfn and cmd bytes */
+	if (msg->data_size < 2) {
+		comp = IPMI_REQ_LEN_INVALID_ERR;
+		goto err;
+	}
+
+	spin_lock_irqsave(&smi->msg_lock, flags);
+
+	if (smi->cur_msg) {
+		comp = IPMI_NODE_BUSY_ERR;
+		goto err_unlock;
+	}
+
+	/* format our data for the OPAL API */
+	opal_msg = smi->opal_msg;
+	opal_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1;
+	opal_msg->netfn = msg->data[0];
+	opal_msg->cmd = msg->data[1];
+	if (msg->data_size > 2)
+		memcpy(opal_msg->data, msg->data + 2, msg->data_size - 2);
+
+	/* data_size already includes the netfn and cmd bytes */
+	size = sizeof(*opal_msg) + msg->data_size - 2;
+
+	pr_devel("%s: opal_ipmi_send(0x%llx, %p, %ld)\n", __func__,
+			smi->interface_id, opal_msg, size);
+	rc = opal_ipmi_send(smi->interface_id, opal_msg, size);
+	pr_devel("%s:  -> %d\n", __func__, rc);
+
+	if (!rc) {
+		smi->cur_msg = msg;
+		spin_unlock_irqrestore(&smi->msg_lock, flags);
+		return;
+	}
+
+	comp = IPMI_ERR_UNSPECIFIED;
+err_unlock:
+	spin_unlock_irqrestore(&smi->msg_lock, flags);
+err:
+	send_error_reply(smi, msg, comp);
+}
+
+static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi)
+{
+	struct opal_ipmi_msg *opal_msg;
+	struct ipmi_smi_msg *msg;
+	unsigned long flags;
+	uint64_t size;
+	int rc;
+
+	pr_devel("%s: opal_ipmi_recv(%llx, msg, sz)\n", __func__,
+			smi->interface_id);
+
+	spin_lock_irqsave(&smi->msg_lock, flags);
+
+	if (!smi->cur_msg) {
+		pr_warn("no current message?\n");
+		return 0;
+	}
+
+	msg = smi->cur_msg;
+	opal_msg = smi->opal_msg;
+
+	size = cpu_to_be64(sizeof(*opal_msg) + IPMI_MAX_MSG_LENGTH);
+
+	rc = opal_ipmi_recv(smi->interface_id,
+			opal_msg,
+			&size);
+	size = be64_to_cpu(size);
+	pr_devel("%s:   -> %d (size %lld)\n", __func__,
+			rc, rc == 0 ? size : 0);
+	if (rc) {
+		spin_unlock_irqrestore(&smi->msg_lock, flags);
+		ipmi_free_smi_msg(msg);
+		return 0;
+	}
+
+	if (size < sizeof(*opal_msg)) {
+		spin_unlock_irqrestore(&smi->msg_lock, flags);
+		pr_warn("unexpected IPMI message size %lld\n", size);
+		return 0;
+	}
+
+	if (opal_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) {
+		spin_unlock_irqrestore(&smi->msg_lock, flags);
+		pr_warn("unexpected IPMI message format (version %d)\n",
+				opal_msg->version);
+		return 0;
+	}
+
+	msg->rsp[0] = opal_msg->netfn;
+	msg->rsp[1] = opal_msg->cmd;
+	if (size > sizeof(*opal_msg))
+		memcpy(&msg->rsp[2], opal_msg->data, size - sizeof(*opal_msg));
+	msg->rsp_size = 2 + size - sizeof(*opal_msg);
+
+	smi->cur_msg = NULL;
+	spin_unlock_irqrestore(&smi->msg_lock, flags);
+	ipmi_smi_msg_received(smi->intf, msg);
+	return 0;
+}
+
+static void ipmi_powernv_request_events(void *send_info)
+{
+}
+
+static void ipmi_powernv_set_run_to_completion(void *send_info,
+		bool run_to_completion)
+{
+}
+
+static void ipmi_powernv_poll(void *send_info)
+{
+	struct ipmi_smi_powernv *smi = send_info;
+	ipmi_powernv_recv(smi);
+}
+
+static struct ipmi_smi_handlers ipmi_powernv_smi_handlers = {
+	.owner			= THIS_MODULE,
+	.start_processing	= ipmi_powernv_start_processing,
+	.sender			= ipmi_powernv_send,
+	.request_events		= ipmi_powernv_request_events,
+	.set_run_to_completion	= ipmi_powernv_set_run_to_completion,
+	.poll			= ipmi_powernv_poll,
+};
+
+static int ipmi_opal_event(struct notifier_block *nb,
+			  unsigned long events, void *change)
+{
+	struct ipmi_smi_powernv *smi = container_of(nb,
+					struct ipmi_smi_powernv, event_nb);
+
+	if (events & smi->event)
+		ipmi_powernv_recv(smi);
+	return 0;
+}
+
+static int ipmi_powernv_probe(struct platform_device *pdev)
+{
+	struct ipmi_smi_powernv *ipmi;
+	struct device *dev;
+	u32 prop;
+	int rc;
+
+	if (!pdev || !pdev->dev.of_node)
+		return -ENODEV;
+
+	dev = &pdev->dev;
+
+	ipmi = devm_kzalloc(dev, sizeof(*ipmi), GFP_KERNEL);
+	if (!ipmi)
+		return -ENOMEM;
+
+	spin_lock_init(&ipmi->msg_lock);
+
+	rc = of_property_read_u32(dev->of_node, "ibm,ipmi-interface-id",
+			&prop);
+	if (rc) {
+		dev_warn(dev, "No interface ID property\n");
+		goto err_free;
+	}
+	ipmi->interface_id = prop;
+
+	rc = of_property_read_u32(dev->of_node, "interrupts", &prop);
+	if (rc) {
+		dev_warn(dev, "No interrupts property\n");
+		goto err_free;
+	}
+
+	ipmi->event = 1ull << prop;
+	ipmi->event_nb.notifier_call = ipmi_opal_event;
+
+	rc = opal_notifier_register(&ipmi->event_nb);
+	if (rc) {
+		dev_warn(dev, "OPAL notifier registration failed (%d)\n", rc);
+		goto err_free;
+	}
+
+	ipmi->opal_msg = devm_kmalloc(dev,
+			sizeof(*ipmi->opal_msg) + IPMI_MAX_MSG_LENGTH,
+			GFP_KERNEL);
+	if (!ipmi->opal_msg) {
+		rc = -ENOMEM;
+		goto err_unregister;
+	}
+
+	/* todo: query actual ipmi_device_id */
+	rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi,
+			&ipmi->ipmi_id, dev, "powernv", 0);
+	if (rc) {
+		dev_warn(dev, "IPMI SMI registration failed (%d)\n", rc);
+		goto err_free_msg;
+	}
+
+	dev_set_drvdata(dev, ipmi);
+	return 0;
+
+err_free_msg:
+	devm_kfree(dev, ipmi->opal_msg);
+err_unregister:
+	opal_notifier_unregister(&ipmi->event_nb);
+err_free:
+	devm_kfree(dev, ipmi);
+	return rc;
+}
+
+static int ipmi_powernv_remove(struct platform_device *pdev)
+{
+	struct ipmi_smi_powernv *smi = dev_get_drvdata(&pdev->dev);
+	ipmi_unregister_smi(smi->intf);
+	opal_notifier_unregister(&smi->event_nb);
+	return 0;
+}
+
+static const struct of_device_id ipmi_powernv_match[] = {
+	{ .compatible = "ibm,opal-ipmi" },
+	{ },
+};
+
+
+static struct platform_driver powernv_ipmi_driver = {
+	.driver = {
+		.name		= "ipmi-powernv",
+		.owner		= THIS_MODULE,
+		.of_match_table	= ipmi_powernv_match,
+	},
+	.probe	= ipmi_powernv_probe,
+	.remove	= ipmi_powernv_remove,
+};
+
+
+module_platform_driver(powernv_ipmi_driver);
+
+MODULE_DEVICE_TABLE(of, ipmi_powernv_match);
+MODULE_DESCRIPTION("powernv IPMI driver");
+MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>");
+MODULE_LICENSE("GPL");

^ permalink raw reply related

* [PATCH net-next] PPC: bpf_jit_comp: add SKF_AD_HATYPE instruction
From: Denis Kirjanov @ 2014-11-06  6:02 UTC (permalink / raw)
  To: netdev
  Cc: Philippe Bergheaud, linuxppc-dev, Denis Kirjanov, Daniel Borkmann,
	Alexei Starovoitov

Add BPF extension SKF_AD_HATYPE to ppc JIT to check
the hw type of the interface

JIT off:
[   69.106783] test_bpf: #20 LD_HATYPE 48 48 PASS
JIT on:
[   64.721757] test_bpf: #20 LD_HATYPE 7 6 PASS

CC: Alexei Starovoitov<alexei.starovoitov@gmail.com>
CC: Daniel Borkmann<dborkman@redhat.com>
CC: Philippe Bergheaud<felix@linux.vnet.ibm.com>
Signed-off-by: Denis Kirjanov <kda@linux-powerpc.org>
---
 arch/powerpc/net/bpf_jit_comp.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index d110e28..8bf4fc2 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -412,6 +412,22 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
 			PPC_ANDI(r_A, r_A, PKT_TYPE_MAX);
 			PPC_SRWI(r_A, r_A, 5);
 			break;
+		case BPF_ANC | SKF_AD_HATYPE:
+			BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2);
+			PPC_LD_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff,
+								dev));
+			PPC_CMPDI(r_scratch1, 0);
+			if (ctx->pc_ret0 != -1) {
+				PPC_BCC(COND_EQ, addrs[ctx->pc_ret0]);
+			} else {
+				/* Exit, returning 0; first pass hits here. */
+				PPC_BCC_SHORT(COND_NE, (ctx->idx*4)+12);
+				PPC_LI(r_ret, 0);
+				PPC_JMP(exit_addr);
+			}
+			PPC_LHZ_OFFS(r_A, r_scratch1,
+				     offsetof(struct net_device, type));
+			break;
 		case BPF_ANC | SKF_AD_CPU:
 #ifdef CONFIG_SMP
 			/*
-- 
2.1.0

^ permalink raw reply related

* Re: [PATCH] macintosh: therm_pm72: delete deprecated driver
From: Wolfram Sang @ 2014-11-06  9:25 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1415240376.4925.2.camel@kernel.crashing.org>

[-- Attachment #1: Type: text/plain, Size: 567 bytes --]

On Thu, Nov 06, 2014 at 01:19:36PM +1100, Benjamin Herrenschmidt wrote:
> On Thu, 2014-11-06 at 02:45 +0100, Wolfram Sang wrote:
> > The new driver is around for more than 2 years now, so the old one can
> > go. Getting rid of it helps the removal of the legacy .attach_adapter
> > callback of the I2C subsystem.
> > 
> > Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
> 
> Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Thanks! I can take it via my I2C tree, but I'd think it makes more sense
if you take it via ppc? What do you prefer?


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* Re: [PATCH] macintosh: therm_pm72: delete deprecated driver
From: Benjamin Herrenschmidt @ 2014-11-06  9:50 UTC (permalink / raw)
  To: Wolfram Sang; +Cc: linuxppc-dev, Michael Ellerman
In-Reply-To: <20141106092551.GA1333@katana>

On Thu, 2014-11-06 at 10:25 +0100, Wolfram Sang wrote:
> On Thu, Nov 06, 2014 at 01:19:36PM +1100, Benjamin Herrenschmidt wrote:
> > On Thu, 2014-11-06 at 02:45 +0100, Wolfram Sang wrote:
> > > The new driver is around for more than 2 years now, so the old one can
> > > go. Getting rid of it helps the removal of the legacy .attach_adapter
> > > callback of the I2C subsystem.
> > > 
> > > Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
> > 
> > Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> Thanks! I can take it via my I2C tree, but I'd think it makes more sense
> if you take it via ppc? What do you prefer?

Either works. These days Michael Ellerman handles the day to day
maintainance of the powerpc tree so it's his choice but if you need this
as a dependency on your subsequent API removal it's probably better that
you take it.

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH] macintosh: therm_pm72: delete deprecated driver
From: Wolfram Sang @ 2014-11-06  9:56 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, Michael Ellerman
In-Reply-To: <1415267404.4925.15.camel@kernel.crashing.org>

[-- Attachment #1: Type: text/plain, Size: 1093 bytes --]

On Thu, Nov 06, 2014 at 08:50:04PM +1100, Benjamin Herrenschmidt wrote:
> On Thu, 2014-11-06 at 10:25 +0100, Wolfram Sang wrote:
> > On Thu, Nov 06, 2014 at 01:19:36PM +1100, Benjamin Herrenschmidt wrote:
> > > On Thu, 2014-11-06 at 02:45 +0100, Wolfram Sang wrote:
> > > > The new driver is around for more than 2 years now, so the old one can
> > > > go. Getting rid of it helps the removal of the legacy .attach_adapter
> > > > callback of the I2C subsystem.
> > > > 
> > > > Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
> > > 
> > > Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > 
> > Thanks! I can take it via my I2C tree, but I'd think it makes more sense
> > if you take it via ppc? What do you prefer?
> 
> Either works. These days Michael Ellerman handles the day to day
> maintainance of the powerpc tree so it's his choice but if you need this
> as a dependency on your subsequent API removal it's probably better that
> you take it.

No dependency, just a first step. Good to know, I will CC Michael on
(somewhen) upcoming patches, too.


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* [PATCH] powerpc/iommu: Rename iommu_[un]map_sg functions
From: Joerg Roedel @ 2014-11-06 12:32 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Arnd Bergmann
  Cc: cbe-oss-dev, linuxppc-dev, linux-kernel

Hi,

here is a patch that fixes a compile failure on powerpc with the recent
iommu tree. If this patch is okay with you guys I'd like to carry it in
the iommu tree too.

Thanks,

	Joerg

>From ff39a0301d01ad24f7097718b4ec8215eb0c1141 Mon Sep 17 00:00:00 2001
From: Joerg Roedel <jroedel@suse.de>
Date: Wed, 5 Nov 2014 15:28:30 +0100
Subject: [PATCH] powerpc/iommu: Rename iommu_[un]map_sg functions

The IOMMU-API gained support for a new iommu_map_sg
function. This causes compile failures on powerpc because
the function name is already globally used there.
This patch renames adds a ppc_ prefix to these functions to
solve the compile problem.

Signed-off-by: Joerg Roedel <jroedel@suse.de>
---
 arch/powerpc/include/asm/iommu.h    | 17 ++++++++++-------
 arch/powerpc/kernel/dma-iommu.c     |  8 ++++----
 arch/powerpc/kernel/iommu.c         | 16 ++++++++--------
 arch/powerpc/platforms/cell/iommu.c |  9 +++++----
 4 files changed, 27 insertions(+), 23 deletions(-)

diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index 42632c7..9cfa370 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -137,13 +137,16 @@ static inline void set_iommu_table_base_and_group(struct device *dev,
 	iommu_add_device(dev);
 }
 
-extern int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
-			struct scatterlist *sglist, int nelems,
-			unsigned long mask, enum dma_data_direction direction,
-			struct dma_attrs *attrs);
-extern void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
-			   int nelems, enum dma_data_direction direction,
-			   struct dma_attrs *attrs);
+extern int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
+			    struct scatterlist *sglist, int nelems,
+			    unsigned long mask,
+			    enum dma_data_direction direction,
+			    struct dma_attrs *attrs);
+extern void ppc_iommu_unmap_sg(struct iommu_table *tbl,
+			       struct scatterlist *sglist,
+			       int nelems,
+			       enum dma_data_direction direction,
+			       struct dma_attrs *attrs);
 
 extern void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl,
 				  size_t size, dma_addr_t *dma_handle,
diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c
index 54d0116..4c68bfe 100644
--- a/arch/powerpc/kernel/dma-iommu.c
+++ b/arch/powerpc/kernel/dma-iommu.c
@@ -60,16 +60,16 @@ static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
 			    int nelems, enum dma_data_direction direction,
 			    struct dma_attrs *attrs)
 {
-	return iommu_map_sg(dev, get_iommu_table_base(dev), sglist, nelems,
-			    device_to_mask(dev), direction, attrs);
+	return ppc_iommu_map_sg(dev, get_iommu_table_base(dev), sglist, nelems,
+				device_to_mask(dev), direction, attrs);
 }
 
 static void dma_iommu_unmap_sg(struct device *dev, struct scatterlist *sglist,
 		int nelems, enum dma_data_direction direction,
 		struct dma_attrs *attrs)
 {
-	iommu_unmap_sg(get_iommu_table_base(dev), sglist, nelems, direction,
-		       attrs);
+	ppc_iommu_unmap_sg(get_iommu_table_base(dev), sglist, nelems,
+			   direction, attrs);
 }
 
 /* We support DMA to/from any memory page via the iommu */
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index a10642a..a83cf5e 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -428,10 +428,10 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
 		ppc_md.tce_flush(tbl);
 }
 
-int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
-		 struct scatterlist *sglist, int nelems,
-		 unsigned long mask, enum dma_data_direction direction,
-		 struct dma_attrs *attrs)
+int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
+		     struct scatterlist *sglist, int nelems,
+		     unsigned long mask, enum dma_data_direction direction,
+		     struct dma_attrs *attrs)
 {
 	dma_addr_t dma_next = 0, dma_addr;
 	struct scatterlist *s, *outs, *segstart;
@@ -539,7 +539,7 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
 
 	DBG("mapped %d elements:\n", outcount);
 
-	/* For the sake of iommu_unmap_sg, we clear out the length in the
+	/* For the sake of ppc_iommu_unmap_sg, we clear out the length in the
 	 * next entry of the sglist if we didn't fill the list completely
 	 */
 	if (outcount < incount) {
@@ -572,9 +572,9 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
 }
 
 
-void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
-		int nelems, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+void ppc_iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
+			int nelems, enum dma_data_direction direction,
+			struct dma_attrs *attrs)
 {
 	struct scatterlist *sg;
 
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index 2b90ff8..c7c8720 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -621,8 +621,9 @@ static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg,
 	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
 		return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs);
 	else
-		return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents,
-				    device_to_mask(dev), direction, attrs);
+		return ppc_iommu_map_sg(dev, cell_get_iommu_table(dev), sg,
+					nents, device_to_mask(dev),
+					direction, attrs);
 }
 
 static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg,
@@ -632,8 +633,8 @@ static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg,
 	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
 		dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs);
 	else
-		iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction,
-			       attrs);
+		ppc_iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents,
+				   direction, attrs);
 }
 
 static int dma_fixed_dma_supported(struct device *dev, u64 mask)
-- 
1.8.4.5

^ permalink raw reply related

* Re: [PATCH 0/2] Add IPMI support for powernv powerpc machines
From: Corey Minyard @ 2014-11-06 14:15 UTC (permalink / raw)
  To: Jeremy Kerr, openipmi-developer, linuxppc-dev
In-Reply-To: <1415245107.292153.444216242273.0.gpush@pablo>

On 11/05/2014 09:38 PM, Jeremy Kerr wrote:
> This series adds IPMI driver and arch glue for OPAL-firmware-based
> powernv machines. The first change exposes the firmware's IPMI API, and
> the second adds an actual driver.
>
> IPMI folks: the IPMI driver could do with a little review, as it's not a
> conventional BT/KCS/SMI SI, in that the low-level send/recv interface
> will handle the entire message at once.

Handling the entire message at once should be fine, as that's what this
driver level is designed to do for the message handler.  That part all
looks correct.  The code itself looks good, but I have a couple of
high-level comments.

The driver at this level can receive more than one message to handle at
a time, so it needs some sort of queue.  This is to allow multiple users
and to allow the message handler to send its own commands while other
commands are going on.  You might argue that the queuing should be done
in ipmi_msghandler, and you would probably be right.  I'll look at doing
that.  If that is the case, then your NULL check for current message
should probably be a BUG_ON().

Do you need to handle any BMC flags?  Particularly incoming events?


> Corey & Michael: if this is acceptable, it may be mergable as two
> separate patches - one for the IPMI subsystem, one for the powernv
> platform.  However, we'd need to preserve their order: patch 2/2 depends
> on 1/2, which provides the structure & function definitions. This'll
> break the build if only 2/2 is in the tree, and CONFIG_IPMI_POWERNV is
> set.
>
> Alternatively, they could be merged by one maintainer, pending an ack
> from the other.

I'm fine either way.

Thanks,

-corey

> Comments / queries / etc welcome.
>
> Cheers,
>
>
> Jeremy
>
> ---
> Jeremy Kerr (2):
>       powerpc/powernv: Add OPAL IPMI interface
>       drivers/char/ipmi: Add powernv IPMI driver
>

^ permalink raw reply

* Re: [PATCH] hwrng: pseries - port to new read API and fix stack corruption
From: Herbert Xu @ 2014-11-06 15:13 UTC (permalink / raw)
  To: Greg Kurz; +Cc: linuxppc-dev, linux-kernel, stable
In-Reply-To: <20141031063233.1884.86309.stgit@bahia.local>

On Fri, Oct 31, 2014 at 07:50:11AM +0100, Greg Kurz wrote:
> The add_early_randomness() function in drivers/char/hw_random/core.c passes
> a 16-byte buffer to pseries_rng_data_read(). Unfortunately, plpar_hcall()
> returns four 64-bit values and trashes 16 bytes on the stack.
> 
> This bug has been lying around for a long time. It got unveiled by:
> 
> commit d3cc7996473a7bdd33256029988ea690754e4e2a
> Author: Amit Shah <amit.shah@redhat.com>
> Date:   Thu Jul 10 15:42:34 2014 +0530
> 
>     hwrng: fetch randomness only after device init
> 
> It may trig a oops while loading or unloading the pseries-rng module for both
> PowerVM and PowerKVM guests.
> 
> This patch does two things:
> - pass an intermediate well sized buffer to plpar_hcall(). This is acceptalbe
>   since we're not on a hot path.
> - move to the new read API so that we know the return buffer size for sure.
> 
> Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com>

Patch applied to crypto.

Thanks,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [PATCH 1/3] powerpc/dts: Factorize the clock control node
From: Emil Medve @ 2014-11-06 15:34 UTC (permalink / raw)
  To: Scott Wood; +Cc: yuantian.tang, linuxppc-dev, devicetree
In-Reply-To: <1414711297.23458.173.camel@snotra.buserror.net>

Hello Scott,


On 10/30/2014 06:21 PM, Scott Wood wrote:
>>> I don't think the mux stuff belongs here, given that clockgen2.dtsi
>>> doesn't have it, and I saw at least one clockgen1 user needing to
>>> supplement this with more muxes.
>>
>> The intent was to put here devices/nodes that are common per chassis
>> from the low to high end. Specific SoC would change/augment this as
>> appropriate. I could have put each node in its own file as we've done
>> elsewhere, but I thought it would be too much
>>
>> Yes, chassis v1 and v2 have differences, but that's not unexpected
> 
> It just strikes me as being an awkward split of where each mux node
> goes.  Is it guaranteed by the chassis that all v1 will have at least
> the first two muxes?

As far as I'm aware we're not building chassis v1 SoC(s) anymore


Cheers,

^ permalink raw reply

* [PATCH v2 1/3] powerpc/dts: Factorize the clock control node
From: Emil Medve @ 2014-11-06 15:48 UTC (permalink / raw)
  To: scottwood, yuantian.tang, linuxppc-dev, devicetree; +Cc: Emil Medve

Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com>
Change-Id: I25ce24a25862b4ca460164159867abefe00ccdd1
---

v2: Whitespace fixes

 arch/powerpc/boot/dts/b4860emu.dts             |  4 +-
 arch/powerpc/boot/dts/fsl/b4420si-post.dtsi    | 28 +--------
 arch/powerpc/boot/dts/fsl/b4860si-post.dtsi    | 28 +--------
 arch/powerpc/boot/dts/fsl/p2041si-post.dtsi    | 48 +---------------
 arch/powerpc/boot/dts/fsl/p3041si-post.dtsi    | 48 +---------------
 arch/powerpc/boot/dts/fsl/p4080si-post.dtsi    | 48 +---------------
 arch/powerpc/boot/dts/fsl/p5020si-post.dtsi    | 48 +---------------
 arch/powerpc/boot/dts/fsl/p5040si-post.dtsi    | 48 +---------------
 arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi | 78 ++++++++++++++++++++++++++
 arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi | 61 ++++++++++++++++++++
 arch/powerpc/boot/dts/fsl/t1040si-post.dtsi    | 30 +---------
 arch/powerpc/boot/dts/fsl/t2081si-post.dtsi    | 29 +---------
 arch/powerpc/boot/dts/fsl/t4240si-post.dtsi    | 29 +---------
 arch/powerpc/boot/dts/t4240emu.dts             |  4 +-
 14 files changed, 163 insertions(+), 368 deletions(-)
 create mode 100644 arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi
 create mode 100644 arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi

diff --git a/arch/powerpc/boot/dts/b4860emu.dts b/arch/powerpc/boot/dts/b4860emu.dts
index 85646b4..2aa5cd3 100644
--- a/arch/powerpc/boot/dts/b4860emu.dts
+++ b/arch/powerpc/boot/dts/b4860emu.dts
@@ -193,9 +193,9 @@
 		fsl,liodn-bits = <12>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "fsl/qoriq-clockgen2.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,b4-clockgen", "fsl,qoriq-clockgen-2.0";
-		reg = <0xe1000 0x1000>;
 	};
 
 /include/ "fsl/qoriq-dma-0.dtsi"
diff --git a/arch/powerpc/boot/dts/fsl/b4420si-post.dtsi b/arch/powerpc/boot/dts/fsl/b4420si-post.dtsi
index b34c62e..aced374 100644
--- a/arch/powerpc/boot/dts/fsl/b4420si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/b4420si-post.dtsi
@@ -80,33 +80,9 @@
 		compatible = "fsl,b4420-device-config", "fsl,qoriq-device-config-2.0";
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen2.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,b4420-clockgen", "fsl,qoriq-clockgen-2.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-2.0";
-			clock-output-names = "sysclk";
-		};
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 0x4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2", "pll0-div4";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 0x4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2", "pll1-div4";
-		};
 
 		mux0: mux0@0 {
 			#clock-cells = <0>;
diff --git a/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi b/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi
index 256fac8..3b9bb37 100644
--- a/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi
@@ -273,33 +273,9 @@
 		compatible = "fsl,b4860-device-config", "fsl,qoriq-device-config-2.0";
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen2.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,b4860-clockgen", "fsl,qoriq-clockgen-2.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-2.0";
-			clock-output-names = "sysclk";
-		};
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 0x4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2", "pll0-div4";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 0x4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2", "pll1-div4";
-		};
 
 		mux0: mux0@0 {
 			#clock-cells = <0>;
diff --git a/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi b/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi
index f297514..3320341 100644
--- a/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi
@@ -313,53 +313,9 @@
 		#sleep-cells = <2>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen1.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,p2041-clockgen", "fsl,qoriq-clockgen-1.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		reg = <0xe1000 0x1000>;
-		clock-frequency = <0>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-1.0";
-			clock-output-names = "sysclk";
-		};
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2";
-		};
-
-		mux0: mux0@0 {
-			#clock-cells = <0>;
-			reg = <0x0 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux0";
-		};
-
-		mux1: mux1@20 {
-			#clock-cells = <0>;
-			reg = <0x20 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux1";
-		};
 
 		mux2: mux2@40 {
 			#clock-cells = <0>;
diff --git a/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi b/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi
index b3f89ce..b28964f 100644
--- a/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi
@@ -340,53 +340,9 @@
 		#sleep-cells = <2>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen1.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,p3041-clockgen", "fsl,qoriq-clockgen-1.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		reg = <0xe1000 0x1000>;
-		clock-frequency = <0>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-1.0";
-			clock-output-names = "sysclk";
-		};
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2";
-		};
-
-		mux0: mux0@0 {
-			#clock-cells = <0>;
-			reg = <0x0 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux0";
-		};
-
-		mux1: mux1@20 {
-			#clock-cells = <0>;
-			reg = <0x20 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux1";
-		};
 
 		mux2: mux2@40 {
 			#clock-cells = <0>;
diff --git a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi
index e410b15..eb043a7 100644
--- a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi
@@ -360,35 +360,9 @@
 		#sleep-cells = <2>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen1.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,p4080-clockgen", "fsl,qoriq-clockgen-1.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		reg = <0xe1000 0x1000>;
-		clock-frequency = <0>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-1.0";
-			clock-output-names = "sysclk";
-		};
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2";
-		};
 
 		pll2: pll2@840 {
 			#clock-cells = <1>;
@@ -406,24 +380,6 @@
 			clock-output-names = "pll3", "pll3-div2";
 		};
 
-		mux0: mux0@0 {
-			#clock-cells = <0>;
-			reg = <0x0 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux0";
-		};
-
-		mux1: mux1@20 {
-			#clock-cells = <0>;
-			reg = <0x20 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux1";
-		};
-
 		mux2: mux2@40 {
 			#clock-cells = <0>;
 			reg = <0x40 0x4>;
diff --git a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi
index d07c771..b70c8c3 100644
--- a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi
@@ -345,53 +345,9 @@
 		#sleep-cells = <2>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen1.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,p5020-clockgen", "fsl,qoriq-clockgen-1.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		reg = <0xe1000 0x1000>;
-		clock-frequency = <0>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-1.0";
-			clock-output-names = "sysclk";
-		};
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2";
-		};
-
-		mux0: mux0@0 {
-			#clock-cells = <0>;
-			reg = <0x0 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux0";
-		};
-
-		mux1: mux1@20 {
-			#clock-cells = <0>;
-			reg = <0x20 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux1";
-		};
 	};
 
 	rcpm: global-utilities@e2000 {
diff --git a/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi b/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi
index 243574b..b459b7e 100644
--- a/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi
@@ -305,53 +305,9 @@
 		#sleep-cells = <2>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen1.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,p5040-clockgen", "fsl,qoriq-clockgen-1.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		reg = <0xe1000 0x1000>;
-		clock-frequency = <0>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-1.0";
-			clock-output-names = "sysclk";
-		};
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 0x4>;
-			compatible = "fsl,qoriq-core-pll-1.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2";
-		};
-
-		mux0: mux0@0 {
-			#clock-cells = <0>;
-			reg = <0x0 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux0";
-		};
-
-		mux1: mux1@20 {
-			#clock-cells = <0>;
-			reg = <0x20 0x4>;
-			compatible = "fsl,qoriq-core-mux-1.0";
-			clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
-			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
-			clock-output-names = "cmux1";
-		};
 
 		mux2: mux2@40 {
 			#clock-cells = <0>;
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi
new file mode 100644
index 0000000..4871048
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi
@@ -0,0 +1,78 @@
+/*
+ * QorIQ clock control device tree stub [ controller @ offset 0xe1000 ]
+ *
+ * Copyright 2014 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *	 notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *	 notice, this list of conditions and the following disclaimer in the
+ *	 documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *	 names of its contributors may be used to endorse or promote products
+ *	 derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+global-utilities@e1000 {
+	compatible = "fsl,qoriq-clockgen-1.0";
+	ranges = <0x0 0xe1000 0x1000>;
+	reg = <0xe1000 0x1000>;
+	clock-frequency = <0>;
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	sysclk: sysclk {
+		#clock-cells = <0>;
+		compatible = "fsl,qoriq-sysclk-1.0", "fixed-clock";
+		clock-output-names = "sysclk";
+	};
+	pll0: pll0@800 {
+		#clock-cells = <1>;
+		reg = <0x800 0x4>;
+		compatible = "fsl,qoriq-core-pll-1.0";
+		clocks = <&sysclk>;
+		clock-output-names = "pll0", "pll0-div2";
+	};
+	pll1: pll1@820 {
+		#clock-cells = <1>;
+		reg = <0x820 0x4>;
+		compatible = "fsl,qoriq-core-pll-1.0";
+		clocks = <&sysclk>;
+		clock-output-names = "pll1", "pll1-div2";
+	};
+	mux0: mux0@0 {
+		#clock-cells = <0>;
+		reg = <0x0 0x4>;
+		compatible = "fsl,qoriq-core-mux-1.0";
+		clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
+		clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
+		clock-output-names = "cmux0";
+	};
+	mux1: mux1@20 {
+		#clock-cells = <0>;
+		reg = <0x20 0x4>;
+		compatible = "fsl,qoriq-core-mux-1.0";
+		clocks = <&pll0 0>, <&pll0 1>, <&pll1 0>, <&pll1 1>;
+		clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
+		clock-output-names = "cmux1";
+	};
+};
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi
new file mode 100644
index 0000000..5d18d2a
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi
@@ -0,0 +1,61 @@
+/*
+ * QorIQ clock control device tree stub [ controller @ offset 0xe1000 ]
+ *
+ * Copyright 2014 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *	 notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *	 notice, this list of conditions and the following disclaimer in the
+ *	 documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *	 names of its contributors may be used to endorse or promote products
+ *	 derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+global-utilities@e1000 {
+	compatible = "fsl,qoriq-clockgen-2.0";
+	ranges = <0x0 0xe1000 0x1000>;
+	reg = <0xe1000 0x1000>;
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	sysclk: sysclk {
+		#clock-cells = <0>;
+		compatible = "fsl,qoriq-sysclk-2.0", "fixed-clock";
+		clock-output-names = "sysclk";
+	};
+	pll0: pll0@800 {
+		#clock-cells = <1>;
+		reg = <0x800 0x4>;
+		compatible = "fsl,qoriq-core-pll-2.0";
+		clocks = <&sysclk>;
+		clock-output-names = "pll0", "pll0-div2", "pll0-div4";
+	};
+	pll1: pll1@820 {
+		#clock-cells = <1>;
+		reg = <0x820 0x4>;
+		compatible = "fsl,qoriq-core-pll-2.0";
+		clocks = <&sysclk>;
+		clock-output-names = "pll1", "pll1-div2", "pll1-div4";
+	};
+};
diff --git a/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi b/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi
index e989b6a..b726be1 100644
--- a/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi
@@ -432,35 +432,9 @@
 		fsl,liodn-bits = <12>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen2.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,t1040-clockgen", "fsl,qoriq-clockgen-2.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		reg = <0xe1000 0x1000>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-2.0";
-			clock-output-names = "sysclk", "fixed-clock";
-		};
-
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2", "pll0-div4";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2", "pll1-div4";
-		};
 
 		mux0: mux0@0 {
 			#clock-cells = <0>;
diff --git a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
index dfbe499..b71dd1c 100644
--- a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
@@ -553,34 +553,9 @@
 		fsl,liodn-bits = <12>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen2.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,t2080-clockgen", "fsl,qoriq-clockgen-2.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		reg = <0xe1000 0x1000>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-2.0";
-			clock-output-names = "sysclk", "fixed-clock";
-		};
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2", "pll0-div4";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2", "pll1-div4";
-		};
 
 		mux0: mux0@0 {
 			#clock-cells = <0>;
diff --git a/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi b/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi
index d2ecd86..c0fcd6b 100644
--- a/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi
@@ -1032,34 +1032,9 @@
 		fsl,liodn-bits = <12>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "qoriq-clockgen2.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,t4240-clockgen", "fsl,qoriq-clockgen-2.0";
-		ranges = <0x0 0xe1000 0x1000>;
-		reg = <0xe1000 0x1000>;
-		#address-cells = <1>;
-		#size-cells = <1>;
-
-		sysclk: sysclk {
-			#clock-cells = <0>;
-			compatible = "fsl,qoriq-sysclk-2.0";
-			clock-output-names = "sysclk";
-		};
-
-		pll0: pll0@800 {
-			#clock-cells = <1>;
-			reg = <0x800 0x4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll0", "pll0-div2", "pll0-div4";
-		};
-
-		pll1: pll1@820 {
-			#clock-cells = <1>;
-			reg = <0x820 0x4>;
-			compatible = "fsl,qoriq-core-pll-2.0";
-			clocks = <&sysclk>;
-			clock-output-names = "pll1", "pll1-div2", "pll1-div4";
-		};
 
 		pll2: pll2@840 {
 			#clock-cells = <1>;
diff --git a/arch/powerpc/boot/dts/t4240emu.dts b/arch/powerpc/boot/dts/t4240emu.dts
index bc12127a..decaf35 100644
--- a/arch/powerpc/boot/dts/t4240emu.dts
+++ b/arch/powerpc/boot/dts/t4240emu.dts
@@ -250,9 +250,9 @@
 		fsl,liodn-bits = <12>;
 	};
 
-	clockgen: global-utilities@e1000 {
+/include/ "fsl/qoriq-clockgen2.dtsi"
+	global-utilities@e1000 {
 		compatible = "fsl,t4240-clockgen", "fsl,qoriq-clockgen-2.0";
-		reg = <0xe1000 0x1000>;
 	};
 
 /include/ "fsl/qoriq-dma-0.dtsi"
-- 
2.1.3

^ permalink raw reply related

* [PATCH v2 3/3] powerpc/dts: Add node(s) for the platform PLL
From: Emil Medve @ 2014-11-06 15:48 UTC (permalink / raw)
  To: scottwood, yuantian.tang, linuxppc-dev, devicetree; +Cc: Emil Medve
In-Reply-To: <1415288893-4457-1-git-send-email-Emilian.Medve@Freescale.com>

Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com>
Change-Id: If76cd705a01813abe53396c1486bc13c4289ee92
---
 arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi | 7 +++++++
 arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi | 7 +++++++
 2 files changed, 14 insertions(+)

diff --git a/arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi
index 4871048..4ece1ed 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-clockgen1.dtsi
@@ -75,4 +75,11 @@ global-utilities@e1000 {
 		clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
 		clock-output-names = "cmux1";
 	};
+	platform_pll: platform-pll@c00 {
+		#clock-cells = <1>;
+		reg = <0xc00 0x4>;
+		compatible = "fsl,qoriq-platform-pll-1.0";
+		clocks = <&sysclk>;
+		clock-output-names = "platform-pll", "platform-pll-div2";
+	};
 };
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi
index 5d18d2a..48e0b6e 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-clockgen2.dtsi
@@ -58,4 +58,11 @@ global-utilities@e1000 {
 		clocks = <&sysclk>;
 		clock-output-names = "pll1", "pll1-div2", "pll1-div4";
 	};
+	platform_pll: platform-pll@c00 {
+		#clock-cells = <1>;
+		reg = <0xc00 0x4>;
+		compatible = "fsl,qoriq-platform-pll-2.0";
+		clocks = <&sysclk>;
+		clock-output-names = "platform-pll", "platform-pll-div2";
+	};
 };
-- 
2.1.3

^ permalink raw reply related

* [PATCH v2 2/3] dt/bindings: qoriq-clock: Add binding for the platform PLL
From: Emil Medve @ 2014-11-06 15:48 UTC (permalink / raw)
  To: scottwood, yuantian.tang, linuxppc-dev, devicetree; +Cc: Emil Medve
In-Reply-To: <1415288893-4457-1-git-send-email-Emilian.Medve@Freescale.com>

Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com>
Change-Id: I7950afa9650d15ec7ce2cca89bb2a1e38586d4a5
---

v2: Whitespace fixes

 Documentation/devicetree/bindings/clock/qoriq-clock.txt | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/qoriq-clock.txt b/Documentation/devicetree/bindings/clock/qoriq-clock.txt
index 5666812..266ff9d 100644
--- a/Documentation/devicetree/bindings/clock/qoriq-clock.txt
+++ b/Documentation/devicetree/bindings/clock/qoriq-clock.txt
@@ -62,6 +62,8 @@ Required properties:
 		It takes parent's clock-frequency as its clock.
 	* "fsl,qoriq-sysclk-2.0": for input system clock (v2.0).
 		It takes parent's clock-frequency as its clock.
+	* "fsl,qoriq-platform-pll-1.0" for the platform PLL clock (v1.0)
+	* "fsl,qoriq-platform-pll-2.0" for the platform PLL clock (v2.0)
 - #clock-cells: From common clock binding. The number of cells in a
 	clock-specifier. Should be <0> for "fsl,qoriq-sysclk-[1,2].0"
 	clocks, or <1> for "fsl,qoriq-core-pll-[1,2].0" clocks.
@@ -128,8 +130,16 @@ Example for clock block and clock provider:
 			clock-names = "pll0", "pll0-div2", "pll1", "pll1-div2";
 			clock-output-names = "cmux1";
 		};
+
+		platform-pll: platform-pll@c00 {
+			#clock-cells = <1>;
+			reg = <0xc00 0x4>;
+			compatible = "fsl,qoriq-platform-pll-1.0";
+			clocks = <&sysclk>;
+			clock-output-names = "platform-pll", "platform-pll-div2";
+		};
 	};
-  }
+};
 
 Example for clock consumer:
 
@@ -139,4 +149,4 @@ Example for clock consumer:
 		clocks = <&mux0>;
 		...
 	};
-  }
+};
-- 
2.1.3

^ permalink raw reply related

* [PATCH v5 08/48] kernel: Move pm_power_off to common code
From: Guenter Roeck @ 2014-11-06 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-mips, linux-ia64, linux-sh, linux, sparclinux, linux-s390,
	linux-am33-list, linux-c6x-dev, linux-hexagon, x86, xen-devel,
	Guenter Roeck, linux-xtensa, user-mode-linux-devel, linux-pm,
	adi-buildroot-devel, linux-m68k, user-mode-linux-user,
	linux-metag, linux-arm-kernel, linux-parisc, linux-cris-kernel,
	linux-alpha, linux390, linuxppc-dev
In-Reply-To: <1415292213-28652-1-git-send-email-linux@roeck-us.net>

pm_power_off is defined for all architectures. Move it to common code.

Have all architectures call do_kernel_power_off instead of pm_power_off.
Some architectures point pm_power_off to machine_power_off. For those,
call do_kernel_power_off from machine_power_off instead.

Acked-by: David Vrabel <david.vrabel@citrix.com>
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
Acked-by: Hirokazu Takata <takata@linux-m32r.org>
Acked-by: James Hogan <james.hogan@imgtec.com>
Acked-by: Jesper Nilsson <jesper.nilsson@axis.com>
Acked-by: Max Filippov <jcmvbkbc@gmail.com>
Acked-by: Rafael J. Wysocki <rjw@rjwysocki.net>
Acked-by: Richard Weinberger <richard@nod.at>
Acked-by: Xuetao Guan <gxt@mprc.pku.edu.cn>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v5:
- Rebase to v3.18-rc3
- Update powerpc code to reflect merged power-off handler changes
v4:
- No change
v3:
- Replace poweroff in all newly introduced variables and in text
  with power_off or power-off as appropriate
v2:
- do_kernel_poweroff -> do_kernel_power_off
- have_kernel_poweroff -> have_kernel_power_off

 arch/alpha/kernel/process.c        |  9 +++------
 arch/arc/kernel/reset.c            |  5 +----
 arch/arm/kernel/process.c          |  5 +----
 arch/arm64/kernel/process.c        |  5 +----
 arch/avr32/kernel/process.c        |  6 +-----
 arch/blackfin/kernel/process.c     |  3 ---
 arch/blackfin/kernel/reboot.c      |  2 ++
 arch/c6x/kernel/process.c          |  9 +--------
 arch/cris/kernel/process.c         |  4 +---
 arch/frv/kernel/process.c          |  5 ++---
 arch/hexagon/kernel/reset.c        |  5 ++---
 arch/ia64/kernel/process.c         |  5 +----
 arch/m32r/kernel/process.c         |  8 ++++----
 arch/m68k/kernel/process.c         |  6 +-----
 arch/metag/kernel/process.c        |  6 +-----
 arch/microblaze/kernel/process.c   |  3 ---
 arch/microblaze/kernel/reset.c     |  1 +
 arch/mips/kernel/reset.c           |  6 +-----
 arch/mn10300/kernel/process.c      |  8 ++------
 arch/openrisc/kernel/process.c     |  8 +++++---
 arch/parisc/kernel/process.c       |  8 ++++----
 arch/powerpc/kernel/setup-common.c |  6 +-----
 arch/powerpc/xmon/xmon.c           |  3 +--
 arch/s390/kernel/setup.c           |  8 ++------
 arch/score/kernel/process.c        |  8 ++++----
 arch/sh/kernel/reboot.c            |  6 +-----
 arch/sparc/kernel/process_32.c     | 10 ++--------
 arch/sparc/kernel/reboot.c         |  8 ++------
 arch/tile/kernel/reboot.c          |  7 +++----
 arch/um/kernel/reboot.c            |  2 --
 arch/unicore32/kernel/process.c    |  9 +--------
 arch/x86/kernel/reboot.c           | 11 +++--------
 arch/x86/xen/enlighten.c           |  3 +--
 arch/xtensa/kernel/process.c       |  4 ----
 drivers/parisc/power.c             |  3 +--
 kernel/power/power_off_handler.c   |  9 +++++++++
 kernel/reboot.c                    |  4 ++--
 37 files changed, 68 insertions(+), 150 deletions(-)

diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
index 1941a07..81c43f8 100644
--- a/arch/alpha/kernel/process.c
+++ b/arch/alpha/kernel/process.c
@@ -24,6 +24,7 @@
 #include <linux/vt.h>
 #include <linux/mman.h>
 #include <linux/elfcore.h>
+#include <linux/pm.h>
 #include <linux/reboot.h>
 #include <linux/tty.h>
 #include <linux/console.h>
@@ -40,12 +41,6 @@
 #include "proto.h"
 #include "pci_impl.h"
 
-/*
- * Power off function, if any
- */
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL(pm_power_off);
-
 #ifdef CONFIG_ALPHA_WTINT
 /*
  * Sleep the CPU.
@@ -184,6 +179,8 @@ machine_halt(void)
 void
 machine_power_off(void)
 {
+	do_kernel_power_off();
+
 	common_shutdown(LINUX_REBOOT_CMD_POWER_OFF, NULL);
 }
 
diff --git a/arch/arc/kernel/reset.c b/arch/arc/kernel/reset.c
index 2768fa1..0758d9d 100644
--- a/arch/arc/kernel/reset.c
+++ b/arch/arc/kernel/reset.c
@@ -26,9 +26,6 @@ void machine_restart(char *__unused)
 
 void machine_power_off(void)
 {
-	/* FIXME ::  power off ??? */
+	do_kernel_power_off();
 	machine_halt();
 }
-
-void (*pm_power_off) (void) = NULL;
-EXPORT_SYMBOL(pm_power_off);
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index fe972a2..aa3f656 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -117,8 +117,6 @@ void soft_restart(unsigned long addr)
 /*
  * Function pointers to optional machine specific functions
  */
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
 
 void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
 
@@ -205,8 +203,7 @@ void machine_power_off(void)
 	local_irq_disable();
 	smp_send_stop();
 
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 }
 
 /*
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index fde9923..6f623a0 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -68,8 +68,6 @@ void soft_restart(unsigned long addr)
 /*
  * Function pointers to optional machine specific functions
  */
-void (*pm_power_off)(void);
-EXPORT_SYMBOL_GPL(pm_power_off);
 
 void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
 
@@ -129,8 +127,7 @@ void machine_power_off(void)
 {
 	local_irq_disable();
 	smp_send_stop();
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 }
 
 /*
diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c
index 42a53e74..529c1f6 100644
--- a/arch/avr32/kernel/process.c
+++ b/arch/avr32/kernel/process.c
@@ -23,9 +23,6 @@
 
 #include <mach/pm.h>
 
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
 /*
  * This file handles the architecture-dependent parts of process handling..
  */
@@ -48,8 +45,7 @@ void machine_halt(void)
 
 void machine_power_off(void)
 {
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 }
 
 void machine_restart(char *cmd)
diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c
index 4aa5545..812dd83 100644
--- a/arch/blackfin/kernel/process.c
+++ b/arch/blackfin/kernel/process.c
@@ -39,9 +39,6 @@ int nr_l1stack_tasks;
 void *l1_stack_base;
 unsigned long l1_stack_len;
 
-void (*pm_power_off)(void) = NULL;
-EXPORT_SYMBOL(pm_power_off);
-
 /*
  * The idle loop on BFIN
  */
diff --git a/arch/blackfin/kernel/reboot.c b/arch/blackfin/kernel/reboot.c
index c4f50a3..387d610 100644
--- a/arch/blackfin/kernel/reboot.c
+++ b/arch/blackfin/kernel/reboot.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/interrupt.h>
+#include <linux/pm.h>
 #include <asm/bfin-global.h>
 #include <asm/reboot.h>
 #include <asm/bfrom.h>
@@ -106,6 +107,7 @@ void machine_halt(void)
 __attribute__((weak))
 void native_machine_power_off(void)
 {
+	do_kernel_power_off();
 	idle_with_irq_disabled();
 }
 
diff --git a/arch/c6x/kernel/process.c b/arch/c6x/kernel/process.c
index 57d2ea8..edf7e5a 100644
--- a/arch/c6x/kernel/process.c
+++ b/arch/c6x/kernel/process.c
@@ -27,12 +27,6 @@ void	(*c6x_halt)(void);
 extern asmlinkage void ret_from_fork(void);
 extern asmlinkage void ret_from_kernel_thread(void);
 
-/*
- * power off function, if any
- */
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
 void arch_cpu_idle(void)
 {
 	unsigned long tmp;
@@ -73,8 +67,7 @@ void machine_halt(void)
 
 void machine_power_off(void)
 {
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 	halt_loop();
 }
 
diff --git a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c
index b78498e..9ebd76b 100644
--- a/arch/cris/kernel/process.c
+++ b/arch/cris/kernel/process.c
@@ -31,9 +31,6 @@
 
 extern void default_idle(void);
 
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
 void arch_cpu_idle(void)
 {
 	default_idle();
@@ -60,6 +57,7 @@ void machine_halt(void)
 
 void machine_power_off(void)
 {
+	do_kernel_power_off();
 }
 
 /*
diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c
index 5d40aeb77..502dabb 100644
--- a/arch/frv/kernel/process.c
+++ b/arch/frv/kernel/process.c
@@ -42,9 +42,6 @@ asmlinkage void ret_from_kernel_thread(void);
 
 #include <asm/pgalloc.h>
 
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
 static void core_sleep_idle(void)
 {
 #ifdef LED_DEBUG_SLEEP
@@ -107,6 +104,8 @@ void machine_power_off(void)
 	gdbstub_exit(0);
 #endif
 
+	do_kernel_power_off();
+
 	for (;;);
 }
 
diff --git a/arch/hexagon/kernel/reset.c b/arch/hexagon/kernel/reset.c
index 76483c1..6f607b6 100644
--- a/arch/hexagon/kernel/reset.c
+++ b/arch/hexagon/kernel/reset.c
@@ -16,11 +16,13 @@
  * 02110-1301, USA.
  */
 
+#include <linux/pm.h>
 #include <linux/smp.h>
 #include <asm/hexagon_vm.h>
 
 void machine_power_off(void)
 {
+	do_kernel_power_off();
 	smp_send_stop();
 	__vmstop();
 }
@@ -32,6 +34,3 @@ void machine_halt(void)
 void machine_restart(char *cmd)
 {
 }
-
-void (*pm_power_off)(void) = NULL;
-EXPORT_SYMBOL(pm_power_off);
diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
index b515149..88121a2 100644
--- a/arch/ia64/kernel/process.c
+++ b/arch/ia64/kernel/process.c
@@ -57,8 +57,6 @@ void (*ia64_mark_idle)(int);
 
 unsigned long boot_option_idle_override = IDLE_NO_OVERRIDE;
 EXPORT_SYMBOL(boot_option_idle_override);
-void (*pm_power_off) (void);
-EXPORT_SYMBOL(pm_power_off);
 
 void
 ia64_do_show_stack (struct unw_frame_info *info, void *arg)
@@ -675,8 +673,7 @@ machine_halt (void)
 void
 machine_power_off (void)
 {
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 	machine_halt();
 }
 
diff --git a/arch/m32r/kernel/process.c b/arch/m32r/kernel/process.c
index e69221d..65a037e 100644
--- a/arch/m32r/kernel/process.c
+++ b/arch/m32r/kernel/process.c
@@ -23,6 +23,7 @@
 #include <linux/fs.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/pm.h>
 #include <linux/ptrace.h>
 #include <linux/unistd.h>
 #include <linux/hardirq.h>
@@ -44,9 +45,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
 	return tsk->thread.lr;
 }
 
-void (*pm_power_off)(void) = NULL;
-EXPORT_SYMBOL(pm_power_off);
-
 void machine_restart(char *__unused)
 {
 #if defined(CONFIG_PLAT_MAPPI3)
@@ -67,7 +65,9 @@ void machine_halt(void)
 
 void machine_power_off(void)
 {
-	/* M32R_FIXME */
+	do_kernel_power_off();
+	for (;;)
+		;
 }
 
 void show_regs(struct pt_regs * regs)
diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c
index afe3d6e..bbc0a63 100644
--- a/arch/m68k/kernel/process.c
+++ b/arch/m68k/kernel/process.c
@@ -78,14 +78,10 @@ void machine_halt(void)
 
 void machine_power_off(void)
 {
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 	for (;;);
 }
 
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL(pm_power_off);
-
 void show_regs(struct pt_regs * regs)
 {
 	printk("\n");
diff --git a/arch/metag/kernel/process.c b/arch/metag/kernel/process.c
index 483dff9..8d95773 100644
--- a/arch/metag/kernel/process.c
+++ b/arch/metag/kernel/process.c
@@ -67,9 +67,6 @@ void arch_cpu_idle_dead(void)
 }
 #endif
 
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
 void (*soc_restart)(char *cmd);
 void (*soc_halt)(void);
 
@@ -90,8 +87,7 @@ void machine_halt(void)
 
 void machine_power_off(void)
 {
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 	smp_send_stop();
 	hard_processor_halt(HALT_OK);
 }
diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c
index b2dd371..0ebca36 100644
--- a/arch/microblaze/kernel/process.c
+++ b/arch/microblaze/kernel/process.c
@@ -44,9 +44,6 @@ void show_regs(struct pt_regs *regs)
 				regs->msr, regs->ear, regs->esr, regs->fsr);
 }
 
-void (*pm_power_off)(void) = NULL;
-EXPORT_SYMBOL(pm_power_off);
-
 void flush_thread(void)
 {
 }
diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c
index fbe58c6..2c6b32c 100644
--- a/arch/microblaze/kernel/reset.c
+++ b/arch/microblaze/kernel/reset.c
@@ -103,6 +103,7 @@ void machine_halt(void)
 void machine_power_off(void)
 {
 	pr_notice("Machine power off...\n");
+	do_kernel_power_off();
 	while (1)
 		;
 }
diff --git a/arch/mips/kernel/reset.c b/arch/mips/kernel/reset.c
index 07fc524..09e74d2 100644
--- a/arch/mips/kernel/reset.c
+++ b/arch/mips/kernel/reset.c
@@ -21,9 +21,6 @@
  */
 void (*_machine_restart)(char *command);
 void (*_machine_halt)(void);
-void (*pm_power_off)(void);
-
-EXPORT_SYMBOL(pm_power_off);
 
 void machine_restart(char *command)
 {
@@ -39,6 +36,5 @@ void machine_halt(void)
 
 void machine_power_off(void)
 {
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 }
diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c
index 3707da5..c78b2eb 100644
--- a/arch/mn10300/kernel/process.c
+++ b/arch/mn10300/kernel/process.c
@@ -20,6 +20,7 @@
 #include <linux/user.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
+#include <linux/pm.h>
 #include <linux/reboot.h>
 #include <linux/percpu.h>
 #include <linux/err.h>
@@ -45,12 +46,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
 }
 
 /*
- * power off function, if any
- */
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
-/*
  * On SMP it's slightly faster (but much more power-consuming!)
  * to poll the ->work.need_resched flag instead of waiting for the
  * cross-CPU IPI to arrive. Use this option with caution.
@@ -93,6 +88,7 @@ void machine_power_off(void)
 #ifdef CONFIG_KERNEL_DEBUGGER
 	gdbstub_exit(0);
 #endif
+	do_kernel_power_off();
 }
 
 void show_regs(struct pt_regs *regs)
diff --git a/arch/openrisc/kernel/process.c b/arch/openrisc/kernel/process.c
index 386af25..494afd2 100644
--- a/arch/openrisc/kernel/process.c
+++ b/arch/openrisc/kernel/process.c
@@ -25,6 +25,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mm.h>
+#include <linux/pm.h>
 #include <linux/stddef.h>
 #include <linux/unistd.h>
 #include <linux/ptrace.h>
@@ -51,7 +52,7 @@
  */
 struct thread_info *current_thread_info_set[NR_CPUS] = { &init_thread_info, };
 
-void machine_restart(void)
+void machine_restart(char *cmd)
 {
 	printk(KERN_INFO "*** MACHINE RESTART ***\n");
 	__asm__("l.nop 1");
@@ -72,11 +73,12 @@ void machine_halt(void)
 void machine_power_off(void)
 {
 	printk(KERN_INFO "*** MACHINE POWER OFF ***\n");
+
+	do_kernel_power_off();
+
 	__asm__("l.nop 1");
 }
 
-void (*pm_power_off) (void) = machine_power_off;
-
 /*
  * When a process does an "exec", machine state like FPU and debug
  * registers need to be reset.  This is a hook function for that.
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
index 0bbbf0d..3f5d14a 100644
--- a/arch/parisc/kernel/process.c
+++ b/arch/parisc/kernel/process.c
@@ -41,6 +41,7 @@
 #include <linux/fs.h>
 #include <linux/module.h>
 #include <linux/personality.h>
+#include <linux/pm.h>
 #include <linux/ptrace.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -133,7 +134,9 @@ void machine_power_off(void)
 	pdc_soft_power_button(0);
 	
 	pdc_chassis_send_status(PDC_CHASSIS_DIRECT_SHUTDOWN);
-		
+
+	do_kernel_power_off();
+
 	/* It seems we have no way to power the system off via
 	 * software. The user has to press the button himself. */
 
@@ -141,9 +144,6 @@ void machine_power_off(void)
 	       "Please power this system off now.");
 }
 
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL(pm_power_off);
-
 /*
  * Free current thread data structures etc..
  */
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index 44c8d03..a2efce7 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -139,8 +139,7 @@ void machine_restart(char *cmd)
 void machine_power_off(void)
 {
 	machine_shutdown();
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 #ifdef CONFIG_SMP
 	smp_send_stop();
 #endif
@@ -151,9 +150,6 @@ void machine_power_off(void)
 /* Used by the G5 thermal driver */
 EXPORT_SYMBOL_GPL(machine_power_off);
 
-void (*pm_power_off)(void);
-EXPORT_SYMBOL_GPL(pm_power_off);
-
 void machine_halt(void)
 {
 	machine_shutdown();
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index 506d256..8780178 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -981,8 +981,7 @@ static void bootcmds(void)
 	else if (cmd == 'h')
 		ppc_md.halt();
 	else if (cmd == 'p')
-		if (pm_power_off)
-			pm_power_off();
+		do_kernel_power_off();
 }
 
 static int cpu_cmd(void)
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index e80d9ff..267e025 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -263,13 +263,9 @@ void machine_power_off(void)
 		 */
 		console_unblank();
 	_machine_power_off();
-}
 
-/*
- * Dummy power off function.
- */
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL_GPL(pm_power_off);
+	do_kernel_power_off();
+}
 
 static int __init early_parse_mem(char *p)
 {
diff --git a/arch/score/kernel/process.c b/arch/score/kernel/process.c
index a1519ad3..b76ea67 100644
--- a/arch/score/kernel/process.c
+++ b/arch/score/kernel/process.c
@@ -29,9 +29,6 @@
 #include <linux/pm.h>
 #include <linux/rcupdate.h>
 
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
 /* If or when software machine-restart is implemented, add code here. */
 void machine_restart(char *command) {}
 
@@ -39,7 +36,10 @@ void machine_restart(char *command) {}
 void machine_halt(void) {}
 
 /* If or when software machine-power-off is implemented, add code here. */
-void machine_power_off(void) {}
+void machine_power_off(void)
+{
+	do_kernel_power_off();
+}
 
 void ret_from_fork(void);
 void ret_from_kernel_thread(void);
diff --git a/arch/sh/kernel/reboot.c b/arch/sh/kernel/reboot.c
index 04afe5b..065de12 100644
--- a/arch/sh/kernel/reboot.c
+++ b/arch/sh/kernel/reboot.c
@@ -11,9 +11,6 @@
 #include <asm/tlbflush.h>
 #include <asm/traps.h>
 
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
 #ifdef CONFIG_SUPERH32
 static void watchdog_trigger_immediate(void)
 {
@@ -51,8 +48,7 @@ static void native_machine_shutdown(void)
 
 static void native_machine_power_off(void)
 {
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 }
 
 static void native_machine_halt(void)
diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
index 50e7b62..cb8148a 100644
--- a/arch/sparc/kernel/process_32.c
+++ b/arch/sparc/kernel/process_32.c
@@ -48,14 +48,6 @@
  */
 void (*sparc_idle)(void);
 
-/* 
- * Power-off handler instantiation for pm.h compliance
- * This is done via auxio, but could be used as a fallback
- * handler when auxio is not present-- unused for now...
- */
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL(pm_power_off);
-
 /*
  * sysctl - toggle power-off restriction for serial console 
  * systems in machine_power_off()
@@ -112,6 +104,8 @@ void machine_power_off(void)
 		sbus_writeb(power_register, auxio_power_register);
 	}
 
+	do_kernel_power_off();
+
 	machine_halt();
 }
 
diff --git a/arch/sparc/kernel/reboot.c b/arch/sparc/kernel/reboot.c
index eba7d91..3c0bb03 100644
--- a/arch/sparc/kernel/reboot.c
+++ b/arch/sparc/kernel/reboot.c
@@ -16,17 +16,13 @@
  */
 int scons_pwroff = 1;
 
-/* This isn't actually used, it exists merely to satisfy the
- * reference in kernel/sys.c
- */
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL(pm_power_off);
-
 void machine_power_off(void)
 {
 	if (strcmp(of_console_device->type, "serial") || scons_pwroff)
 		prom_halt_power_off();
 
+	do_kernel_power_off();
+
 	prom_halt();
 }
 
diff --git a/arch/tile/kernel/reboot.c b/arch/tile/kernel/reboot.c
index 6c5d2c0..8ff4a7f 100644
--- a/arch/tile/kernel/reboot.c
+++ b/arch/tile/kernel/reboot.c
@@ -36,6 +36,9 @@ void machine_power_off(void)
 {
 	arch_local_irq_disable_all();
 	smp_send_stop();
+
+	do_kernel_power_off();
+
 	hv_power_off();
 }
 
@@ -45,7 +48,3 @@ void machine_restart(char *cmd)
 	smp_send_stop();
 	hv_restart((HV_VirtAddr) "vmlinux", (HV_VirtAddr) cmd);
 }
-
-/* No interesting distinction to be made here. */
-void (*pm_power_off)(void) = NULL;
-EXPORT_SYMBOL(pm_power_off);
diff --git a/arch/um/kernel/reboot.c b/arch/um/kernel/reboot.c
index ced8903..a82ef28 100644
--- a/arch/um/kernel/reboot.c
+++ b/arch/um/kernel/reboot.c
@@ -11,8 +11,6 @@
 #include <os.h>
 #include <skas.h>
 
-void (*pm_power_off)(void);
-
 static void kill_off_processes(void)
 {
 	if (proc_mm)
diff --git a/arch/unicore32/kernel/process.c b/arch/unicore32/kernel/process.c
index b008e99..9490dd5 100644
--- a/arch/unicore32/kernel/process.c
+++ b/arch/unicore32/kernel/process.c
@@ -56,16 +56,9 @@ void machine_halt(void)
 	gpio_set_value(GPO_SOFT_OFF, 0);
 }
 
-/*
- * Function pointers to optional machine specific functions
- */
-void (*pm_power_off)(void) = NULL;
-EXPORT_SYMBOL(pm_power_off);
-
 void machine_power_off(void)
 {
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 	machine_halt();
 }
 
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 17962e6..5c09e28 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -30,12 +30,6 @@
 #include <asm/x86_init.h>
 #include <asm/efi.h>
 
-/*
- * Power off function, if any
- */
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
 static const struct desc_ptr no_idt = {};
 
 /*
@@ -647,11 +641,12 @@ static void native_machine_halt(void)
 
 static void native_machine_power_off(void)
 {
-	if (pm_power_off) {
+	if (have_kernel_power_off()) {
 		if (!reboot_force)
 			machine_shutdown();
-		pm_power_off();
+		do_kernel_power_off();
 	}
+
 	/* A fallback in case there is no PM info available */
 	tboot_shutdown(TB_SHUTDOWN_HALT);
 }
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index fac5e4f..bc08998 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -1320,8 +1320,7 @@ static void xen_machine_halt(void)
 
 static void xen_machine_power_off(void)
 {
-	if (pm_power_off)
-		pm_power_off();
+	do_kernel_power_off();
 	xen_reboot(SHUTDOWN_poweroff);
 }
 
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
index 1c85323..c487296 100644
--- a/arch/xtensa/kernel/process.c
+++ b/arch/xtensa/kernel/process.c
@@ -49,10 +49,6 @@ extern void ret_from_kernel_thread(void);
 
 struct task_struct *current_set[NR_CPUS] = {&init_task, };
 
-void (*pm_power_off)(void) = NULL;
-EXPORT_SYMBOL(pm_power_off);
-
-
 #if XTENSA_HAVE_COPROCESSORS
 
 void coprocessor_release_all(struct thread_info *ti)
diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c
index ef31b77..f10cf92 100644
--- a/drivers/parisc/power.c
+++ b/drivers/parisc/power.c
@@ -95,8 +95,7 @@ static void process_shutdown(void)
 		/* send kill signal */
 		if (kill_cad_pid(SIGINT, 1)) {
 			/* just in case killing init process failed */
-			if (pm_power_off)
-				pm_power_off();
+			kernel_power_off();
 		}
 	}
 }
diff --git a/kernel/power/power_off_handler.c b/kernel/power/power_off_handler.c
index e576534..e283ea1 100644
--- a/kernel/power/power_off_handler.c
+++ b/kernel/power/power_off_handler.c
@@ -23,6 +23,12 @@
 #include <linux/types.h>
 
 /*
+ * If set, calling this function will power off the system immediately.
+ */
+void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
+
+/*
  * List of handlers for kernel code which wants to be called
  * to power off the system.
  */
@@ -272,6 +278,9 @@ void do_kernel_power_off(void)
 	 * that risk.
 	 */
 
+	if (pm_power_off)
+		pm_power_off();
+
 	p = rcu_dereference_raw(power_off_handler_list);
 	while (p) {
 		next_p = rcu_dereference_raw(p->next);
diff --git a/kernel/reboot.c b/kernel/reboot.c
index 5925f5a..d87d921 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -306,9 +306,9 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
 		return ret;
 
 	/* Instead of trying to make the power_off code look like
-	 * halt when pm_power_off is not set do it the easy way.
+	 * halt when no power-off handler exists do it the easy way.
 	 */
-	if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
+	if (cmd == LINUX_REBOOT_CMD_POWER_OFF && !have_kernel_power_off())
 		cmd = LINUX_REBOOT_CMD_HALT;
 
 	mutex_lock(&reboot_mutex);
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 41/48] powerpc: Register with kernel power-off handler
From: Guenter Roeck @ 2014-11-06 16:43 UTC (permalink / raw)
  To: linux-kernel
  Cc: cbe-oss-dev, Arnd Bergmann, linux-pm, Geoff Levand,
	Alexander Graf, Paul Mackerras, Alistair Popple, Scott Wood,
	Anatolij Gustschin, linuxppc-dev, Guenter Roeck
In-Reply-To: <1415292213-28652-1-git-send-email-linux@roeck-us.net>

Register with kernel power-off handler instead of setting pm_power_off
directly.

If there is an indication that there can be more than one power-off handler,
use register_power_off_handler, otherwise use register_power_off_handler_simple
to register the power-off handler.

If the power-off handler only resets or stops the system, select the fallback
priority to indicate that the power-off handler is one of last resort.
If the power-off handler powers off the system, select the default priority,
unless the power-off handler installation code suggests that there can be
more than one power-off handler and the new handler is only installed
conditionally. In this case, install the handler with low priority.

Cc: Alexander Graf <agraf@suse.de>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
v5:
- New patch

 arch/powerpc/platforms/44x/ppc476.c              |  3 ++-
 arch/powerpc/platforms/52xx/efika.c              |  3 ++-
 arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c   | 30 ++++++++++++++++--------
 arch/powerpc/platforms/85xx/corenet_generic.c    |  3 ++-
 arch/powerpc/platforms/85xx/sgy_cts1000.c        | 16 +++++++++++--
 arch/powerpc/platforms/cell/celleb_setup.c       |  6 +++--
 arch/powerpc/platforms/cell/qpace_setup.c        |  3 ++-
 arch/powerpc/platforms/cell/setup.c              |  3 ++-
 arch/powerpc/platforms/chrp/setup.c              |  3 ++-
 arch/powerpc/platforms/embedded6xx/gamecube.c    |  3 ++-
 arch/powerpc/platforms/embedded6xx/linkstation.c |  3 ++-
 arch/powerpc/platforms/embedded6xx/wii.c         |  3 ++-
 arch/powerpc/platforms/maple/setup.c             |  6 +++--
 arch/powerpc/platforms/powermac/setup.c          |  3 ++-
 arch/powerpc/platforms/powernv/setup.c           |  6 +++--
 arch/powerpc/platforms/ps3/setup.c               |  3 ++-
 arch/powerpc/platforms/pseries/setup.c           |  3 ++-
 arch/powerpc/sysdev/fsl_soc.c                    |  6 ++---
 18 files changed, 73 insertions(+), 33 deletions(-)

diff --git a/arch/powerpc/platforms/44x/ppc476.c b/arch/powerpc/platforms/44x/ppc476.c
index c11ce65..590d31f 100644
--- a/arch/powerpc/platforms/44x/ppc476.c
+++ b/arch/powerpc/platforms/44x/ppc476.c
@@ -94,7 +94,8 @@ static int avr_probe(struct i2c_client *client,
 {
 	avr_i2c_client = client;
 	ppc_md.restart = avr_reset_system;
-	pm_power_off = avr_power_off_system;
+	register_power_off_handler_simple(avr_power_off_system,
+					  POWER_OFF_PRIORITY_DEFAULT);
 	return 0;
 }
 
diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c
index 6af651e..321a7a7 100644
--- a/arch/powerpc/platforms/52xx/efika.c
+++ b/arch/powerpc/platforms/52xx/efika.c
@@ -212,7 +212,8 @@ static int __init efika_probe(void)
 	DMA_MODE_READ = 0x44;
 	DMA_MODE_WRITE = 0x48;
 
-	pm_power_off = rtas_power_off;
+	register_power_off_handler_simple(rtas_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c
index 15e8021..1bf6b0e 100644
--- a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c
+++ b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c
@@ -19,6 +19,7 @@
 #include <linux/gpio.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/pm.h>
 #include <linux/slab.h>
 #include <linux/kthread.h>
 #include <linux/reboot.h>
@@ -86,7 +87,7 @@ static ssize_t show_status(struct device *d,
 }
 static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
 
-static void mcu_power_off(void)
+static void mcu_power_off(struct power_off_handler_block *this)
 {
 	struct mcu *mcu = glob_mcu;
 
@@ -97,6 +98,11 @@ static void mcu_power_off(void)
 	mutex_unlock(&mcu->lock);
 }
 
+static struct power_off_handler_block mcu_power_off_hb = {
+	.handler = mcu_power_off,
+	.priority = POWER_OFF_PRIORITY_LOW,
+};
+
 static void mcu_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
 {
 	struct mcu *mcu = container_of(gc, struct mcu, gc);
@@ -167,13 +173,15 @@ static int mcu_probe(struct i2c_client *client, const struct i2c_device_id *id)
 	if (ret)
 		goto err;
 
-	/* XXX: this is potentially racy, but there is no lock for pm_power_off */
-	if (!pm_power_off) {
-		glob_mcu = mcu;
-		pm_power_off = mcu_power_off;
-		dev_info(&client->dev, "will provide power-off service\n");
+	glob_mcu = mcu;
+	ret = register_power_off_handler(&mcu_power_off_hb);
+	if (ret) {
+		dev_err(&client->dev, "Failed to register power-off handler\n");
+		goto err_handler;
 	}
 
+	dev_info(&client->dev, "will provide power-off service\n");
+
 	if (device_create_file(&client->dev, &dev_attr_status))
 		dev_err(&client->dev,
 			"couldn't create device file for status\n");
@@ -182,6 +190,10 @@ static int mcu_probe(struct i2c_client *client, const struct i2c_device_id *id)
 				      "mcu-i2c-shdn");
 
 	return 0;
+
+err_handler:
+	mcu_gpiochip_remove(mcu);
+	glob_mcu = NULL;
 err:
 	kfree(mcu);
 	return ret;
@@ -196,10 +208,8 @@ static int mcu_remove(struct i2c_client *client)
 
 	device_remove_file(&client->dev, &dev_attr_status);
 
-	if (glob_mcu == mcu) {
-		pm_power_off = NULL;
-		glob_mcu = NULL;
-	}
+	unregister_power_off_handler(&mcu_power_off_hb);
+	glob_mcu = NULL;
 
 	ret = mcu_gpiochip_remove(mcu);
 	if (ret)
diff --git a/arch/powerpc/platforms/85xx/corenet_generic.c b/arch/powerpc/platforms/85xx/corenet_generic.c
index 1f309cc..f0c4b51 100644
--- a/arch/powerpc/platforms/85xx/corenet_generic.c
+++ b/arch/powerpc/platforms/85xx/corenet_generic.c
@@ -170,7 +170,8 @@ static int __init corenet_generic_probe(void)
 
 			ppc_md.get_irq = ehv_pic_get_irq;
 			ppc_md.restart = fsl_hv_restart;
-			pm_power_off = fsl_hv_halt;
+			register_power_off_handler_simple(fsl_hv_halt,
+						POWER_OFF_PRIORITY_DEFAULT);
 			ppc_md.halt = fsl_hv_halt;
 #ifdef CONFIG_SMP
 			/*
diff --git a/arch/powerpc/platforms/85xx/sgy_cts1000.c b/arch/powerpc/platforms/85xx/sgy_cts1000.c
index e149c9e..d849e8f 100644
--- a/arch/powerpc/platforms/85xx/sgy_cts1000.c
+++ b/arch/powerpc/platforms/85xx/sgy_cts1000.c
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include <linux/of_gpio.h>
 #include <linux/of_irq.h>
+#include <linux/pm.h>
 #include <linux/workqueue.h>
 #include <linux/reboot.h>
 #include <linux/interrupt.h>
@@ -59,6 +60,16 @@ static void gpio_halt_cb(void)
 	gpio_set_value(gpio, trigger);
 }
 
+static void gpio_power_off_cb(struct power_off_handler_block *this)
+{
+	gpio_halt_cb();
+}
+
+static struct power_off_handler_block gpio_power_off_hb = {
+	.handler = gpio_power_off_cb,
+	.priority = POWER_OFF_PRIORITY_DEFAULT,
+};
+
 /* This IRQ means someone pressed the power button and it is waiting for us
  * to handle the shutdown/poweroff. */
 static irqreturn_t gpio_halt_irq(int irq, void *__data)
@@ -120,7 +131,8 @@ static int gpio_halt_probe(struct platform_device *pdev)
 
 	/* Register our halt function */
 	ppc_md.halt = gpio_halt_cb;
-	pm_power_off = gpio_halt_cb;
+	if (register_power_off_handler(&gpio_power_off_hb))
+		pr_warn("gpio-halt: Failed to register power-off handler\n");
 
 	printk(KERN_INFO "gpio-halt: registered GPIO %d (%d trigger, %d"
 	       " irq).\n", gpio, trigger, irq);
@@ -137,7 +149,7 @@ static int gpio_halt_remove(struct platform_device *pdev)
 		free_irq(irq, halt_node);
 
 		ppc_md.halt = NULL;
-		pm_power_off = NULL;
+		unregister_power_off_handler(&gpio_power_off_hb);
 
 		gpio_free(gpio);
 
diff --git a/arch/powerpc/platforms/cell/celleb_setup.c b/arch/powerpc/platforms/cell/celleb_setup.c
index 90be8ec..e301b1a 100644
--- a/arch/powerpc/platforms/cell/celleb_setup.c
+++ b/arch/powerpc/platforms/cell/celleb_setup.c
@@ -142,7 +142,8 @@ static int __init celleb_probe_beat(void)
 	powerpc_firmware_features |= FW_FEATURE_CELLEB_ALWAYS
 		| FW_FEATURE_BEAT | FW_FEATURE_LPAR;
 	hpte_init_beat_v3();
-	pm_power_off = beat_power_off;
+	register_power_off_handler_simple(beat_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
@@ -191,7 +192,8 @@ static int __init celleb_probe_native(void)
 
 	powerpc_firmware_features |= FW_FEATURE_CELLEB_ALWAYS;
 	hpte_init_native();
-	pm_power_off = rtas_power_off;
+	register_power_off_handler_simple(rtas_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/cell/qpace_setup.c b/arch/powerpc/platforms/cell/qpace_setup.c
index d328140..223d40a 100644
--- a/arch/powerpc/platforms/cell/qpace_setup.c
+++ b/arch/powerpc/platforms/cell/qpace_setup.c
@@ -127,7 +127,8 @@ static int __init qpace_probe(void)
 		return 0;
 
 	hpte_init_native();
-	pm_power_off = rtas_power_off;
+	register_power_off_handler_simple(rtas_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c
index d62aa98..ea5460c 100644
--- a/arch/powerpc/platforms/cell/setup.c
+++ b/arch/powerpc/platforms/cell/setup.c
@@ -259,7 +259,8 @@ static int __init cell_probe(void)
 		return 0;
 
 	hpte_init_native();
-	pm_power_off = rtas_power_off;
+	register_power_off_handler_simple(rtas_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c
index 860a59e..0c0288e 100644
--- a/arch/powerpc/platforms/chrp/setup.c
+++ b/arch/powerpc/platforms/chrp/setup.c
@@ -585,7 +585,8 @@ static int __init chrp_probe(void)
 	DMA_MODE_READ = 0x44;
 	DMA_MODE_WRITE = 0x48;
 
-	pm_power_off = rtas_power_off;
+	register_power_off_handler_simple(rtas_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/embedded6xx/gamecube.c b/arch/powerpc/platforms/embedded6xx/gamecube.c
index fe0ed6e..af40f0f 100644
--- a/arch/powerpc/platforms/embedded6xx/gamecube.c
+++ b/arch/powerpc/platforms/embedded6xx/gamecube.c
@@ -67,7 +67,8 @@ static int __init gamecube_probe(void)
 	if (!of_flat_dt_is_compatible(dt_root, "nintendo,gamecube"))
 		return 0;
 
-	pm_power_off = gamecube_power_off;
+	register_power_off_handler_simple(gamecube_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/embedded6xx/linkstation.c b/arch/powerpc/platforms/embedded6xx/linkstation.c
index 540eeb5..0c4dcf8 100644
--- a/arch/powerpc/platforms/embedded6xx/linkstation.c
+++ b/arch/powerpc/platforms/embedded6xx/linkstation.c
@@ -148,7 +148,8 @@ static int __init linkstation_probe(void)
 	if (!of_flat_dt_is_compatible(root, "linkstation"))
 		return 0;
 
-	pm_power_off = linkstation_power_off;
+	register_power_off_handler_simple(linkstation_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/embedded6xx/wii.c b/arch/powerpc/platforms/embedded6xx/wii.c
index 352592d..eea8824 100644
--- a/arch/powerpc/platforms/embedded6xx/wii.c
+++ b/arch/powerpc/platforms/embedded6xx/wii.c
@@ -211,7 +211,8 @@ static int __init wii_probe(void)
 	if (!of_flat_dt_is_compatible(dt_root, "nintendo,wii"))
 		return 0;
 
-	pm_power_off = wii_power_off;
+	register_power_off_handler_simple(wii_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c
index 56b85cd..73c3988 100644
--- a/arch/powerpc/platforms/maple/setup.c
+++ b/arch/powerpc/platforms/maple/setup.c
@@ -169,7 +169,8 @@ static void __init maple_use_rtas_reboot_and_halt_if_present(void)
 	if (rtas_service_present("system-reboot") &&
 	    rtas_service_present("power-off")) {
 		ppc_md.restart = rtas_restart;
-		pm_power_off = rtas_power_off;
+		register_power_off_handler_simple(rtas_power_off,
+						  POWER_OFF_PRIORITY_DEFAULT);
 		ppc_md.halt = rtas_halt;
 	}
 }
@@ -312,7 +313,8 @@ static int __init maple_probe(void)
 	alloc_dart_table();
 
 	hpte_init_native();
-	pm_power_off = maple_power_off;
+	register_power_off_handler_simple(maple_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c
index 713d36d..6496ae4 100644
--- a/arch/powerpc/platforms/powermac/setup.c
+++ b/arch/powerpc/platforms/powermac/setup.c
@@ -632,7 +632,8 @@ static int __init pmac_probe(void)
 	smu_cmdbuf_abs = memblock_alloc_base(4096, 4096, 0x80000000UL);
 #endif /* CONFIG_PMAC_SMU */
 
-	pm_power_off = pmac_power_off;
+	register_power_off_handler_simple(pmac_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	return 1;
 }
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 941831d..a03c41b 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -268,7 +268,8 @@ static void __init pnv_setup_machdep_opal(void)
 	ppc_md.get_rtc_time = opal_get_rtc_time;
 	ppc_md.set_rtc_time = opal_set_rtc_time;
 	ppc_md.restart = pnv_restart;
-	pm_power_off = pnv_power_off;
+	register_power_off_handler_simple(pnv_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 	ppc_md.halt = pnv_halt;
 	ppc_md.machine_check_exception = opal_machine_check;
 	ppc_md.mce_check_early_recovery = opal_mce_check_early_recovery;
@@ -285,7 +286,8 @@ static void __init pnv_setup_machdep_rtas(void)
 		ppc_md.set_rtc_time = rtas_set_rtc_time;
 	}
 	ppc_md.restart = rtas_restart;
-	pm_power_off = rtas_power_off;
+	register_power_off_handler_simple(rtas_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 	ppc_md.halt = rtas_halt;
 }
 #endif /* CONFIG_PPC_POWERNV_RTAS */
diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c
index 009a200..77e0dea 100644
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -248,7 +248,8 @@ static int __init ps3_probe(void)
 	ps3_mm_init();
 	ps3_mm_vas_create(&htab_size);
 	ps3_hpte_init(htab_size);
-	pm_power_off = ps3_power_off;
+	register_power_off_handler_simple(ps3_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	DBG(" <- %s:%d\n", __func__, __LINE__);
 	return 1;
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index db0fc0c..b3c85bb 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -769,7 +769,8 @@ static int __init pSeries_probe(void)
 	else
 		hpte_init_native();
 
-	pm_power_off = pseries_power_off;
+	register_power_off_handler_simple(pseries_power_off,
+					  POWER_OFF_PRIORITY_DEFAULT);
 
 	pr_debug("Machine is%s LPAR !\n",
 	         (powerpc_firmware_features & FW_FEATURE_LPAR) ? "" : " not");
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 1e04568..eeb075f 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -238,9 +238,9 @@ void fsl_hv_restart(char *cmd)
 /*
  * Halt the current partition
  *
- * This function should be assigned to the pm_power_off and ppc_md.halt
- * function pointers, to shut down the partition when we're running under
- * the Freescale hypervisor.
+ * This function should be registered as power-off handler and be assigned
+ * to the ppc_md.halt function pointer, to shut down the partition when
+ * we're running under the Freescale hypervisor.
  */
 void fsl_hv_halt(void)
 {
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH v3 1/4] dt/bindings: Introduce the FSL QorIQ DPAA BMan
From: Scott Wood @ 2014-11-06 21:49 UTC (permalink / raw)
  To: Emil Medve
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree,
	Geoff.Thorpe, corbet, linux-doc, linuxppc-dev, robh+dt, galak,
	grant.likely
In-Reply-To: <1415200734-6978-2-git-send-email-Emilian.Medve@Freescale.com>

On Wed, 2014-11-05 at 09:18 -0600, Emil Medve wrote:
> +Devices connected to a BMan instance via Direct Connect Portals (DCP) must link
> +to the respective BMan instance
> +
> +- fsl,bman
> +	Usage:		Required
> +	Value type:	<prop-encoded-array>
> +	Description:	List of phandle and DCP index pairs, to the BMan instance
> +			to which this device is connected via the DCP

Does software need the DCP index (though for QMan there do seem to be a
few registers associated with each DCP)?  Where can I find that info in
the manual?

-Scott

^ permalink raw reply

* Re: [PATCH] i2c-qoriq: modified compatibility for correct prescaler
From: Scott Wood @ 2014-11-06 21:58 UTC (permalink / raw)
  To: Valentin Longchamp
  Cc: Boschung, Rainer, Linux device trees, Brunck, Holger,
	Linux PowerPC Kernel, Linux I2C
In-Reply-To: <5450AC85.40302@keymile.com>

On Wed, 2014-10-29 at 09:59 +0100, Valentin Longchamp wrote:
> On 10/29/2014 12:08 AM, Scott Wood wrote:
> > On Fri, 2014-10-17 at 11:27 +0200, Valentin Longchamp wrote:
> >> With "fsl-i2c" compatibility the i2c frequency is not set
> >> correctly, because it sets no prescaler. According to the AN2919 from
> >> Freescale and the QorIQ (P2041) documentation, the source clock is 1/2
> >> the platform clock. This implies that a prescaler of 2 must be used.
> >>
> >> This changes the compatibility of the qoriq-i2c .dtsi files to pick the
> >> mpc8543, which uses the same driver but sets the correct prescaler.
> >>
> >> Signed-off-by: Rainer Boschung <rainer.boschung@keymile.com>
> >> Signed-off-by: Valentin Longchamp <valentin.longchamp@keymile.com>
> >> ---
> >>
> >>  arch/powerpc/boot/dts/fsl/qoriq-i2c-0.dtsi | 4 ++--
> >>  arch/powerpc/boot/dts/fsl/qoriq-i2c-1.dtsi | 4 ++--
> >>  2 files changed, 4 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/arch/powerpc/boot/dts/fsl/qoriq-i2c-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-i2c-0.dtsi
> >> index 5f9bf7d..aa6c366 100644
> >> --- a/arch/powerpc/boot/dts/fsl/qoriq-i2c-0.dtsi
> >> +++ b/arch/powerpc/boot/dts/fsl/qoriq-i2c-0.dtsi
> >> @@ -36,7 +36,7 @@ i2c@118000 {
> >>  	#address-cells = <1>;
> >>  	#size-cells = <0>;
> >>  	cell-index = <0>;
> >> -	compatible = "fsl-i2c";
> >> +	compatible = "fsl,mpc8543-i2c", "fsl-i2c";
> >>  	reg = <0x118000 0x100>;
> >>  	interrupts = <38 2 0 0>;
> >>  	dfsrr;
> >> @@ -46,7 +46,7 @@ i2c@118100 {
> >>  	#address-cells = <1>;
> >>  	#size-cells = <0>;
> >>  	cell-index = <1>;
> >> -	compatible = "fsl-i2c";
> >> +	compatible = "fsl,mpc8543-i2c", "fsl-i2c";
> >>  	reg = <0x118100 0x100>;
> >>  	interrupts = <38 2 0 0>;
> >>  	dfsrr;
> > 
> > Are all chips that use this dtsi 100% compatible with mpc8543's i2c, or
> > just in ways the Linux driver cares about?
> 
> I have just looked briefly at the mpc8548 RM (covers mpc8543) and its i2c
> controller looks the same as the qoriq's. I cannot however state if they are
> 100% compatible.
> 
> If we wanted to be on the safe side and strict (since we are not sure that the
> hardware is 100% compatible), we maybe should add a fsl,qoriq-i2c compatible to
> the driver that does the same as mpc8543-i2c.

If we're going to change the device tree I'd rather just add a property
to say what the prescaler is.

-Scott

^ permalink raw reply

* [PATCH] powerpc/fsl: Update fman dt binding with clock name and qbman link
From: Scott Wood @ 2014-11-07  3:01 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Scott Wood, Igal Liberman, Emil Medve, devicetree

The clock name "fmanclk" was given in the example, but not specified
in the binding itself.  Made clock-names mandatory as otherwise there's
not much point having it.

Added a reference to the fsl,qman and fsl,bman properties proposed
in http://patchwork.ozlabs.org/patch/407034/ and
http://patchwork.ozlabs.org/patch/407035/

Signed-off-by: Scott Wood <scottwood@freescale.com>
---
This patch is on top of http://patchwork.ozlabs.org/patch/390351/

 Documentation/devicetree/bindings/powerpc/fsl/fman.txt | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/powerpc/fsl/fman.txt b/Documentation/devicetree/bindings/powerpc/fsl/fman.txt
index da8e5f2..edeea16 100644
--- a/Documentation/devicetree/bindings/powerpc/fsl/fman.txt
+++ b/Documentation/devicetree/bindings/powerpc/fsl/fman.txt
@@ -79,12 +79,12 @@ PROPERTIES
 - clocks
 		Usage: required
 		Value type: <prop-encoded-array>
-		Definition: phandle for fman clock.
+		Definition: phandle for the fman input clock.
 
 - clock-names
-		usage: optional
+		usage: required
 		Value type: <stringlist>
-		Definition: A standard property
+		Definition: "fmanclk" for the fman input clock.
 
 - interrupts
 		Usage: required
@@ -104,6 +104,11 @@ PROPERTIES
 		"Work Queue (WQ) Channel Assignments in the QMan" section
 		in DPAA Reference Manual.
 
+- fsl,qman
+- fsl,bman
+		Usage: required
+		Definition: See soc/fsl/qman.txt and soc/fsl/bman.txt
+
 =============================================================================
 FMan MURAM Node
 
-- 
1.9.1

^ permalink raw reply related

* Re: [v4,17/21] powerpc/8xx: set PTE bit 22 off TLBmiss
From: Scott Wood @ 2014-11-07  3:37 UTC (permalink / raw)
  To: LEROY Christophe; +Cc: Paul Mackerras, linuxppc-dev, linux-kernel
In-Reply-To: <20140919083609.A92871AB040@localhost.localdomain>

On Fri, Sep 19, 2014 at 10:36:09AM +0200, LEROY Christophe wrote:
> No need to re-set this bit at each TLB miss. Let's set it in the PTE.
> 
> Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
> ---
> Changes in v2:
> - None
> 
> Changes in v3:
> - Removed PPC405 related macro from PPC8xx specific code
> - PTE_NONE_MASK doesn't need PAGE_ACCESSED in Linux 2.6
> 
> Changes in v4:
> - None
> 
>  arch/powerpc/include/asm/pgtable-ppc32.h | 20 ++++++++++++++++++++
>  arch/powerpc/include/asm/pte-8xx.h       |  7 +++++--
>  arch/powerpc/kernel/head_8xx.S           | 10 ++--------
>  3 files changed, 27 insertions(+), 10 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/pgtable-ppc32.h b/arch/powerpc/include/asm/pgtable-ppc32.h
> index 47edde8..35a9b44 100644
> --- a/arch/powerpc/include/asm/pgtable-ppc32.h
> +++ b/arch/powerpc/include/asm/pgtable-ppc32.h
> @@ -172,6 +172,25 @@ static inline unsigned long pte_update(pte_t *p,
>  #ifdef PTE_ATOMIC_UPDATES
>  	unsigned long old, tmp;
>  
> +#ifdef CONFIG_PPC_8xx
> +	unsigned long tmp2;
> +
> +	__asm__ __volatile__("\
> +1:	lwarx	%0,0,%4\n\
> +	andc	%1,%0,%5\n\
> +	or	%1,%1,%6\n\
> +	/* 0x200 == Extended encoding, bit 22 */ \
> +	/* Bit 22 has to be 1 if neither _PAGE_USER nor _PAGE_RW are set */ \
> +	rlwimi	%1,%1,32-2,0x200\n /* get _PAGE_USER */ \
> +	rlwinm	%3,%1,32-1,0x200\n /* get _PAGE_RW */ \
> +	or	%1,%3,%1\n\
> +	xori	%1,%1,0x200\n"
> +"	stwcx.	%1,0,%4\n\
> +	bne-	1b"

Why do you need this...

> diff --git a/arch/powerpc/include/asm/pte-8xx.h b/arch/powerpc/include/asm/pte-8xx.h
> index d44826e..daa4616 100644
> --- a/arch/powerpc/include/asm/pte-8xx.h
> +++ b/arch/powerpc/include/asm/pte-8xx.h
> @@ -48,19 +48,22 @@
>   */
>  #define _PAGE_RW	0x0400	/* lsb PP bits, inverted in HW */
>  #define _PAGE_USER	0x0800	/* msb PP bits */
> +/* set when neither _PAGE_USER nor _PAGE_RW are set */
> +#define _PAGE_KNLRO	0x0200
>  
>  #define _PMD_PRESENT	0x0001
>  #define _PMD_BAD	0x0ff0
>  #define _PMD_PAGE_MASK	0x000c
>  #define _PMD_PAGE_8M	0x000c
>  
> -#define _PTE_NONE_MASK _PAGE_ACCESSED
> +#define _PTE_NONE_MASK _PAGE_KNLRO
>  
>  /* Until my rework is finished, 8xx still needs atomic PTE updates */
>  #define PTE_ATOMIC_UPDATES	1
>  
>  /* We need to add _PAGE_SHARED to kernel pages */
> -#define _PAGE_KERNEL_RO	(_PAGE_SHARED)
> +#define _PAGE_KERNEL_RO	(_PAGE_SHARED | _PAGE_KNLRO)
> +#define _PAGE_KERNEL_ROX	(_PAGE_EXEC | _PAGE_KNLRO)
>  #define _PAGE_KERNEL_RW	(_PAGE_DIRTY | _PAGE_RW | _PAGE_HWWRITE)

...if 0x200 is already being set in the PTE here?

-Scott

^ 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