aboutsummaryrefslogblamecommitdiffstats
path: root/arch/powerpc/kernel/prom.c
blob: 6a5b468edb4d2e8021d6fc07807e3a4a6cb8fbb6 (plain) (tree)
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
















































                                                                       
                           






















                                                                 





                                         
                                             






















                                                                

























































































































































































                                                                                     














                                                        





                                                                     
                           


                                

























                                                                              

                                                            



                         





















                                                                            

                                                                           
                        
                                                               
                   




                                                                               
      


                                                                            



                                                                              
                                                                        








                                                                             


































































































































































































































































































































                                                                               



                                                                  























































                                                                               

                                                                      





















































































































































































































































































                                                                                         

                                                              




















                                                                                   
                  

                                                                     




                                                     

                            




                                                                          
                
                                                                       


                                                                          

                                                        

                 
                                                      


                                                                       
                                                                 





                                                                           
                                                                 











                                                                           
                                                                             
















                                                                                     

                                                                             


                               
                                                                        

                         
                               




                                                
                                                                       
                                 
                                                                            


                                   
                                                                      



                                      
                                                                         

                                         
                                                                       










                                                                             


                                                                             



















                                                                                   
                                                              


                                                             
                                                                 

































                                                                                     
                                                                    



                                                 







                                                                       

                         
                                                             




                                          
                                                                      
























































                                                                              
                                                         


                                                

                                                         

                                               








                                                                    

                                                               
           
                                                       
 












































                                                                        
                                                                           




                                                        
                                                                   



















































































































































































































































































































































































































































































































                                                                                
                                          
































































                                                                                    
                                                                    
 
                               

                                







                                                             
                                      
         
                     

                                    
                             


                                                              
                                   

                 



















                                                                                         
                 























































































































































                                                                                                 
                       
/*
 * Procedures for creating, accessing and interpreting the device tree.
 *
 * Paul Mackerras	August 1996.
 * Copyright (C) 1996-2005 Paul Mackerras.
 * 
 *  Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
 *    {engebret|bergner}@us.ibm.com 
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */

#undef DEBUG

#include <stdarg.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/stringify.h>
#include <linux/delay.h>
#include <linux/initrd.h>
#include <linux/bitops.h>
#include <linux/module.h>

#include <asm/prom.h>
#include <asm/rtas.h>
#include <asm/lmb.h>
#include <asm/page.h>
#include <asm/processor.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/system.h>
#include <asm/mmu.h>
#include <asm/pgtable.h>
#include <asm/pci.h>
#include <asm/iommu.h>
#include <asm/btext.h>
#include <asm/sections.h>
#include <asm/machdep.h>
#include <asm/pSeries_reconfig.h>
#include <asm/pci-bridge.h>

#ifdef DEBUG
#define DBG(fmt...) printk(KERN_ERR fmt)
#else
#define DBG(fmt...)
#endif

struct pci_reg_property {
	struct pci_address addr;
	u32 size_hi;
	u32 size_lo;
};

struct isa_reg_property {
	u32 space;
	u32 address;
	u32 size;
};


typedef int interpret_func(struct device_node *, unsigned long *,
			   int, int, int);

static int __initdata dt_root_addr_cells;
static int __initdata dt_root_size_cells;

#ifdef CONFIG_PPC64
static int __initdata iommu_is_off;
int __initdata iommu_force_on;
unsigned long tce_alloc_start, tce_alloc_end;
#endif

typedef u32 cell_t;

#if 0
static struct boot_param_header *initial_boot_params __initdata;
#else
struct boot_param_header *initial_boot_params;
#endif

static struct device_node *allnodes = NULL;

/* use when traversing tree through the allnext, child, sibling,
 * or parent members of struct device_node.
 */
static DEFINE_RWLOCK(devtree_lock);

/* export that to outside world */
struct device_node *of_chosen;

struct device_node *dflt_interrupt_controller;
int num_interrupt_controllers;

/*
 * Wrapper for allocating memory for various data that needs to be
 * attached to device nodes as they are processed at boot or when
 * added to the device tree later (e.g. DLPAR).  At boot there is
 * already a region reserved so we just increment *mem_start by size;
 * otherwise we call kmalloc.
 */
static void * prom_alloc(unsigned long size, unsigned long *mem_start)
{
	unsigned long tmp;

	if (!mem_start)
		return kmalloc(size, GFP_KERNEL);

	tmp = *mem_start;
	*mem_start += size;
	return (void *)tmp;
}

/*
 * Find the device_node with a given phandle.
 */
static struct device_node * find_phandle(phandle ph)
{
	struct device_node *np;

	for (np = allnodes; np != 0; np = np->allnext)
		if (np->linux_phandle == ph)
			return np;
	return NULL;
}

/*
 * Find the interrupt parent of a node.
 */
static struct device_node * __devinit intr_parent(struct device_node *p)
{
	phandle *parp;

	parp = (phandle *) get_property(p, "interrupt-parent", NULL);
	if (parp == NULL)
		return p->parent;
	p = find_phandle(*parp);
	if (p != NULL)
		return p;
	/*
	 * On a powermac booted with BootX, we don't get to know the
	 * phandles for any nodes, so find_phandle will return NULL.
	 * Fortunately these machines only have one interrupt controller
	 * so there isn't in fact any ambiguity.  -- paulus
	 */
	if (num_interrupt_controllers == 1)
		p = dflt_interrupt_controller;
	return p;
}

/*
 * Find out the size of each entry of the interrupts property
 * for a node.
 */
int __devinit prom_n_intr_cells(struct device_node *np)
{
	struct device_node *p;
	unsigned int *icp;

	for (p = np; (p = intr_parent(p)) != NULL; ) {
		icp = (unsigned int *)
			get_property(p, "#interrupt-cells", NULL);
		if (icp != NULL)
			return *icp;
		if (get_property(p, "interrupt-controller", NULL) != NULL
		    || get_property(p, "interrupt-map", NULL) != NULL) {
			printk("oops, node %s doesn't have #interrupt-cells\n",
			       p->full_name);
			return 1;
		}
	}
#ifdef DEBUG_IRQ
	printk("prom_n_intr_cells failed for %s\n", np->full_name);
#endif
	return 1;
}

/*
 * Map an interrupt from a device up to the platform interrupt
 * descriptor.
 */
static int __devinit map_interrupt(unsigned int **irq, struct device_node **ictrler,
				   struct device_node *np, unsigned int *ints,
				   int nintrc)
{
	struct device_node *p, *ipar;
	unsigned int *imap, *imask, *ip;
	int i, imaplen, match;
	int newintrc = 0, newaddrc = 0;
	unsigned int *reg;
	int naddrc;

	reg = (unsigned int *) get_property(np, "reg", NULL);
	naddrc = prom_n_addr_cells(np);
	p = intr_parent(np);
	while (p != NULL) {
		if (get_property(p, "interrupt-controller", NULL) != NULL)
			/* this node is an interrupt controller, stop here */
			break;
		imap = (unsigned int *)
			get_property(p, "interrupt-map", &imaplen);
		if (imap == NULL) {
			p = intr_parent(p);
			continue;
		}
		imask = (unsigned int *)
			get_property(p, "interrupt-map-mask", NULL);
		if (imask == NULL) {
			printk("oops, %s has interrupt-map but no mask\n",
			       p->full_name);
			return 0;
		}
		imaplen /= sizeof(unsigned int);
		match = 0;
		ipar = NULL;
		while (imaplen > 0 && !match) {
			/* check the child-interrupt field */
			match = 1;
			for (i = 0; i < naddrc && match; ++i)
				match = ((reg[i] ^ imap[i]) & imask[i]) == 0;
			for (; i < naddrc + nintrc && match; ++i)
				match = ((ints[i-naddrc] ^ imap[i]) & imask[i]) == 0;
			imap += naddrc + nintrc;
			imaplen -= naddrc + nintrc;
			/* grab the interrupt parent */
			ipar = find_phandle((phandle) *imap++);
			--imaplen;
			if (ipar == NULL && num_interrupt_controllers == 1)
				/* cope with BootX not giving us phandles */
				ipar = dflt_interrupt_controller;
			if (ipar == NULL) {
				printk("oops, no int parent %x in map of %s\n",
				       imap[-1], p->full_name);
				return 0;
			}
			/* find the parent's # addr and intr cells */
			ip = (unsigned int *)
				get_property(ipar, "#interrupt-cells", NULL);
			if (ip == NULL) {
				printk("oops, no #interrupt-cells on %s\n",
				       ipar->full_name);
				return 0;
			}
			newintrc = *ip;
			ip = (unsigned int *)
				get_property(ipar, "#address-cells", NULL);
			newaddrc = (ip == NULL)? 0: *ip;
			imap += newaddrc + newintrc;
			imaplen -= newaddrc + newintrc;
		}
		if (imaplen < 0) {
			printk("oops, error decoding int-map on %s, len=%d\n",
			       p->full_name, imaplen);
			return 0;
		}
		if (!match) {
#ifdef DEBUG_IRQ
			printk("oops, no match in %s int-map for %s\n",
			       p->full_name, np->full_name);
#endif
			return 0;
		}
		p = ipar;
		naddrc = newaddrc;
		nintrc = newintrc;
		ints = imap - nintrc;
		reg = ints - naddrc;
	}
	if (p == NULL) {
#ifdef DEBUG_IRQ
		printk("hmmm, int tree for %s doesn't have ctrler\n",
		       np->full_name);
#endif
		return 0;
	}
	*irq = ints;
	*ictrler = p;
	return nintrc;
}

static unsigned char map_isa_senses[4] = {
	IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE,
	IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE,
	IRQ_SENSE_EDGE  | IRQ_POLARITY_NEGATIVE,
	IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE
};

static unsigned char map_mpic_senses[4] = {
	IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE,
	IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE,
	/* 2 seems to be used for the 8259 cascade... */
	IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE,
	IRQ_SENSE_EDGE  | IRQ_POLARITY_NEGATIVE,
};

static int __devinit finish_node_interrupts(struct device_node *np,
					    unsigned long *mem_start,
					    int measure_only)
{
	unsigned int *ints;
	int intlen, intrcells, intrcount;
	int i, j, n, sense;
	unsigned int *irq, virq;
	struct device_node *ic;

	if (num_interrupt_controllers == 0) {
		/*
		 * Old machines just have a list of interrupt numbers
		 * and no interrupt-controller nodes.
		 */
		ints = (unsigned int *) get_property(np, "AAPL,interrupts",
						     &intlen);
		/* XXX old interpret_pci_props looked in parent too */
		/* XXX old interpret_macio_props looked for interrupts
		   before AAPL,interrupts */
		if (ints == NULL)
			ints = (unsigned int *) get_property(np, "interrupts",
							     &intlen);
		if (ints == NULL)
			return 0;

		np->n_intrs = intlen / sizeof(unsigned int);
		np->intrs = prom_alloc(np->n_intrs * sizeof(np->intrs[0]),
				       mem_start);
		if (!np->intrs)
			return -ENOMEM;
		if (measure_only)
			return 0;

		for (i = 0; i < np->n_intrs; ++i) {
			np->intrs[i].line = *ints++;
			np->intrs[i].sense = IRQ_SENSE_LEVEL
				| IRQ_POLARITY_NEGATIVE;
		}
		return 0;
	}

	ints = (unsigned int *) get_property(np, "interrupts", &intlen);
	if (ints == NULL)
		return 0;
	intrcells = prom_n_intr_cells(np);
	intlen /= intrcells * sizeof(unsigned int);

	np->intrs = prom_alloc(intlen * sizeof(*(np->intrs)), mem_start);
	if (!np->intrs)
		return -ENOMEM;

	if (measure_only)
		return 0;

	intrcount = 0;
	for (i = 0; i < intlen; ++i, ints += intrcells) {
		n = map_interrupt(&irq, &ic, np, ints, intrcells);
		if (n <= 0)
			continue;

		/* don't map IRQ numbers under a cascaded 8259 controller */
		if (ic && device_is_compatible(ic, "chrp,iic")) {
			np->intrs[intrcount].line = irq[0];
			sense = (n > 1)? (irq[1] & 3): 3;
			np->intrs[intrcount].sense = map_isa_senses[sense];
		} else {
			virq = virt_irq_create_mapping(irq[0]);
#ifdef CONFIG_PPC64
			if (virq == NO_IRQ) {
				printk(KERN_CRIT "Could not allocate interrupt"
				       " number for %s\n", np->full_name);
				continue;
			}
#endif
			np->intrs[intrcount].line = irq_offset_up(virq);
			sense = (n > 1)? (irq[1] & 3): 1;
			np->intrs[intrcount].sense = map_mpic_senses[sense];
		}

#ifdef CONFIG_PPC64
		/* We offset irq numbers for the u3 MPIC by 128 in PowerMac */
		if (_machine == PLATFORM_POWERMAC && ic && ic->parent) {
			char *name = get_property(ic->parent, "name", NULL);
			if (name && !strcmp(name, "u3"))
				np->intrs[intrcount].line += 128;
			else if (!(name && !strcmp(name, "mac-io")))
				/* ignore other cascaded controllers, such as
				   the k2-sata-root */
				break;
		}
#endif
		if (n > 2) {
			printk("hmmm, got %d intr cells for %s:", n,
			       np->full_name);
			for (j = 0; j < n; ++j)
				printk(" %d", irq[j]);
			printk("\n");
		}
		++intrcount;
	}
	np->n_intrs = intrcount;

	return 0;
}

static int __devinit interpret_pci_props(struct device_node *np,
					 unsigned long *mem_start,
					 int naddrc, int nsizec,
					 int measure_only)
{
	struct address_range *adr;
	struct pci_reg_property *pci_addrs;
	int i, l, n_addrs;

	pci_addrs = (struct pci_reg_property *)
		get_property(np, "assigned-addresses", &l);
	if (!pci_addrs)
		return 0;

	n_addrs = l / sizeof(*pci_addrs);

	adr = prom_alloc(n_addrs * sizeof(*adr), mem_start);
	if (!adr)
		return -ENOMEM;

 	if (measure_only)
 		return 0;

 	np->addrs = adr;
 	np->n_addrs = n_addrs;

 	for (i = 0; i < n_addrs; i++) {
 		adr[i].space = pci_addrs[i].addr.a_hi;
 		adr[i].address = pci_addrs[i].addr.a_lo |
			((u64)pci_addrs[i].addr.a_mid << 32);
 		adr[i].size = pci_addrs[i].size_lo;
	}

	return 0;
}

static int __init interpret_dbdma_props(struct device_node *np,
					unsigned long *mem_start,
					int naddrc, int nsizec,
					int measure_only)
{
	struct reg_property32 *rp;
	struct address_range *adr;
	unsigned long base_address;
	int i, l;
	struct device_node *db;

	base_address = 0;
	if (!measure_only) {
		for (db = np->parent; db != NULL; db = db->parent) {
			if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) {
				base_address = db->addrs[0].address;
				break;
			}
		}
	}

	rp = (struct reg_property32 *) get_property(np, "reg", &l);
	if (rp != 0 && l >= sizeof(struct reg_property32)) {
		i = 0;
		adr = (struct address_range *) (*mem_start);
		while ((l -= sizeof(struct reg_property32)) >= 0) {
			if (!measure_only) {
				adr[i].space = 2;
				adr[i].address = rp[i].address + base_address;
				adr[i].size = rp[i].size;
			}
			++i;
		}
		np->addrs = adr;
		np->n_addrs = i;
		(*mem_start) += i * sizeof(struct address_range);
	}

	return 0;
}

static int __init interpret_macio_props(struct device_node *np,
					unsigned long *mem_start,
					int naddrc, int nsizec,
					int measure_only)
{
	struct reg_property32 *rp;
	struct address_range *adr;
	unsigned long base_address;
	int i, l;
	struct device_node *db;

	base_address = 0;
	if (!measure_only) {
		for (db = np->parent; db != NULL; db = db->parent) {
			if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) {
				base_address = db->addrs[0].address;
				break;
			}
		}
	}

	rp = (struct reg_property32 *) get_property(np, "reg", &l);
	if (rp != 0 && l >= sizeof(struct reg_property32)) {
		i = 0;
		adr = (struct address_range *) (*mem_start);
		while ((l -= sizeof(struct reg_property32)) >= 0) {
			if (!measure_only) {
				adr[i].space = 2;
				adr[i].address = rp[i].address + base_address;
				adr[i].size = rp[i].size;
			}
			++i;
		}
		np->addrs = adr;
		np->n_addrs = i;
		(*mem_start) += i * sizeof(struct address_range);
	}

	return 0;
}

static int __init interpret_isa_props(struct device_node *np,
				      unsigned long *mem_start,
				      int naddrc, int nsizec,
				      int measure_only)
{
	struct isa_reg_property *rp;
	struct address_range *adr;
	int i, l;

	rp = (struct isa_reg_property *) get_property(np, "reg", &l);
	if (rp != 0 && l >= sizeof(struct isa_reg_property)) {
		i = 0;
		adr = (struct address_range *) (*mem_start);
		while ((l -= sizeof(struct isa_reg_property)) >= 0) {
			if (!measure_only) {
				adr[i].space = rp[i].space;
				adr[i].address = rp[i].address;
				adr[i].size = rp[i].size;
			}
			++i;
		}
		np->addrs = adr;
		np->n_addrs = i;
		(*mem_start) += i * sizeof(struct address_range);
	}

	return 0;
}

static int __init interpret_root_props(struct device_node *np,
				       unsigned long *mem_start,
				       int naddrc, int nsizec,
				       int measure_only)
{
	struct address_range *adr;
	int i, l;
	unsigned int *rp;
	int rpsize = (naddrc + nsizec) * sizeof(unsigned int);

	rp = (unsigned int *) get_property(np, "reg", &l);
	if (rp != 0 && l >= rpsize) {
		i = 0;
		adr = (struct address_range *) (*mem_start);
		while ((l -= rpsize) >= 0) {
			if (!measure_only) {
				adr[i].space = 0;
				adr[i].address = rp[naddrc - 1];
				adr[i].size = rp[naddrc + nsizec - 1];
			}
			++i;
			rp += naddrc + nsizec;
		}
		np->addrs = adr;
		np->n_addrs = i;
		(*mem_start) += i * sizeof(struct address_range);
	}

	return 0;
}

static int __devinit finish_node(struct device_node *np,
				 unsigned long *mem_start,
				 interpret_func *ifunc,
				 int naddrc, int nsizec,
				 int measure_only)
{
	struct device_node *child;
	int *ip, rc = 0;

	/* get the device addresses and interrupts */
	if (ifunc != NULL)
		rc = ifunc(np, mem_start, naddrc, nsizec, measure_only);
	if (rc)
		goto out;

	rc = finish_node_interrupts(np, mem_start, measure_only);
	if (rc)
		goto out;

	/* Look for #address-cells and #size-cells properties. */
	ip = (int *) get_property(np, "#address-cells", NULL);
	if (ip != NULL)
		naddrc = *ip;
	ip = (int *) get_property(np, "#size-cells", NULL);
	if (ip != NULL)
		nsizec = *ip;

	if (!strcmp(np->name, "device-tree") || np->parent == NULL)
		ifunc = interpret_root_props;
	else if (np->type == 0)
		ifunc = NULL;
	else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci"))
		ifunc = interpret_pci_props;
	else if (!strcmp(np->type, "dbdma"))
		ifunc = interpret_dbdma_props;
	else if (!strcmp(np->type, "mac-io") || ifunc == interpret_macio_props)
		ifunc = interpret_macio_props;
	else if (!strcmp(np->type, "isa"))
		ifunc = interpret_isa_props;
	else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3"))
		ifunc = interpret_root_props;
	else if (!((ifunc == interpret_dbdma_props
		    || ifunc == interpret_macio_props)
		   && (!strcmp(np->type, "escc")
		       || !strcmp(np->type, "media-bay"))))
		ifunc = NULL;

	for (child = np->child; child != NULL; child = child->sibling) {
		rc = finish_node(child, mem_start, ifunc,
				 naddrc, nsizec, measure_only);
		if (rc)
			goto out;
	}
out:
	return rc;
}

static void __init scan_interrupt_controllers(void)
{
	struct device_node *np;
	int n = 0;
	char *name, *ic;
	int iclen;

	for (np = allnodes; np != NULL; np = np->allnext) {
		ic = get_property(np, "interrupt-controller", &iclen);
		name = get_property(np, "name", NULL);
		/* checking iclen makes sure we don't get a false
		   match on /chosen.interrupt_controller */
		if ((name != NULL
		     && strcmp(name, "interrupt-controller") == 0)
		    || (ic != NULL && iclen == 0
			&& strcmp(name, "AppleKiwi"))) {
			if (n == 0)
				dflt_interrupt_controller = np;
			++n;
		}
	}
	num_interrupt_controllers = n;
}

/**
 * finish_device_tree is called once things are running normally
 * (i.e. with text and data mapped to the address they were linked at).
 * It traverses the device tree and fills in some of the additional,
 * fields in each node like {n_}addrs and {n_}intrs, the virt interrupt
 * mapping is also initialized at this point.
 */
void __init finish_device_tree(void)
{
	unsigned long start, end, size = 0;

	DBG(" -> finish_device_tree\n");

#ifdef CONFIG_PPC64
	/* Initialize virtual IRQ map */
	virt_irq_init();
#endif
	scan_interrupt_controllers();

	/*
	 * Finish device-tree (pre-parsing some properties etc...)
	 * We do this in 2 passes. One with "measure_only" set, which
	 * will only measure the amount of memory needed, then we can
	 * allocate that memory, and call finish_node again. However,
	 * we must be careful as most routines will fail nowadays when
	 * prom_alloc() returns 0, so we must make sure our first pass
	 * doesn't start at 0. We pre-initialize size to 16 for that
	 * reason and then remove those additional 16 bytes
	 */
	size = 16;
	finish_node(allnodes, &size, NULL, 0, 0, 1);
	size -= 16;
	end = start = (unsigned long) __va(lmb_alloc(size, 128));
	finish_node(allnodes, &end, NULL, 0, 0, 0);
	BUG_ON(end != start + size);

	DBG(" <- finish_device_tree\n");
}

static inline char *find_flat_dt_string(u32 offset)
{
	return ((char *)initial_boot_params) +
		initial_boot_params->off_dt_strings + offset;
}

/**
 * This function is used to scan the flattened device-tree, it is
 * used to extract the memory informations at boot before we can
 * unflatten the tree
 */
int __init of_scan_flat_dt(int (*it)(unsigned long node,
				     const char *uname, int depth,
				     void *data),
			   void *data)
{
	unsigned long p = ((unsigned long)initial_boot_params) +
		initial_boot_params->off_dt_struct;
	int rc = 0;
	int depth = -1;

	do {
		u32 tag = *((u32 *)p);
		char *pathp;
		
		p += 4;
		if (tag == OF_DT_END_NODE) {
			depth --;
			continue;
		}
		if (tag == OF_DT_NOP)
			continue;
		if (tag == OF_DT_END)
			break;
		if (tag == OF_DT_PROP) {
			u32 sz = *((u32 *)p);
			p += 8;
			if (initial_boot_params->version < 0x10)
				p = _ALIGN(p, sz >= 8 ? 8 : 4);
			p += sz;
			p = _ALIGN(p, 4);
			continue;
		}
		if (tag != OF_DT_BEGIN_NODE) {
			printk(KERN_WARNING "Invalid tag %x scanning flattened"
			       " device tree !\n", tag);
			return -EINVAL;
		}
		depth++;
		pathp = (char *)p;
		p = _ALIGN(p + strlen(pathp) + 1, 4);
		if ((*pathp) == '/') {
			char *lp, *np;
			for (lp = NULL, np = pathp; *np; np++)
				if ((*np) == '/')
					lp = np+1;
			if (lp != NULL)
				pathp = lp;
		}
		rc = it(p, pathp, depth, data);
		if (rc != 0)
			break;		
	} while(1);

	return rc;
}

/**
 * This  function can be used within scan_flattened_dt callback to get
 * access to properties
 */
void* __init of_get_flat_dt_prop(unsigned long node, const char *name,
				 unsigned long *size)
{
	unsigned long p = node;

	do {
		u32 tag = *((u32 *)p);
		u32 sz, noff;
		const char *nstr;

		p += 4;
		if (tag == OF_DT_NOP)
			continue;
		if (tag != OF_DT_PROP)
			return NULL;

		sz = *((u32 *)p);
		noff = *((u32 *)(p + 4));
		p += 8;
		if (initial_boot_params->version < 0x10)
			p = _ALIGN(p, sz >= 8 ? 8 : 4);

		nstr = find_flat_dt_string(noff);
		if (nstr == NULL) {
			printk(KERN_WARNING "Can't find property index"
			       " name !\n");
			return NULL;
		}
		if (strcmp(name, nstr) == 0) {
			if (size)
				*size = sz;
			return (void *)p;
		}
		p += sz;
		p = _ALIGN(p, 4);
	} while(1);
}

static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
				       unsigned long align)
{
	void *res;

	*mem = _ALIGN(*mem, align);
	res = (void *)*mem;
	*mem += size;

	return res;
}

static unsigned long __init unflatten_dt_node(unsigned long mem,
					      unsigned long *p,
					      struct device_node *dad,
					      struct device_node ***allnextpp,
					      unsigned long fpsize)
{
	struct device_node *np;
	struct property *pp, **prev_pp = NULL;
	char *pathp;
	u32 tag;
	unsigned int l, allocl;
	int has_name = 0;
	int new_format = 0;

	tag = *((u32 *)(*p));
	if (tag != OF_DT_BEGIN_NODE) {
		printk("Weird tag at start of node: %x\n", tag);
		return mem;
	}
	*p += 4;
	pathp = (char *)*p;
	l = allocl = strlen(pathp) + 1;
	*p = _ALIGN(*p + l, 4);

	/* version 0x10 has a more compact unit name here instead of the full
	 * path. we accumulate the full path size using "fpsize", we'll rebuild
	 * it later. We detect this because the first character of the name is
	 * not '/'.
	 */
	if ((*pathp) != '/') {
		new_format = 1;
		if (fpsize == 0) {
			/* root node: special case. fpsize accounts for path
			 * plus terminating zero. root node only has '/', so
			 * fpsize should be 2, but we want to avoid the first
			 * level nodes to have two '/' so we use fpsize 1 here
			 */
			fpsize = 1;
			allocl = 2;
		} else {
			/* account for '/' and path size minus terminal 0
			 * already in 'l'
			 */
			fpsize += l;
			allocl = fpsize;
		}
	}


	np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
				__alignof__(struct device_node));
	if (allnextpp) {
		memset(np, 0, sizeof(*np));
		np->full_name = ((char*)np) + sizeof(struct device_node);
		if (new_format) {
			char *p = np->full_name;
			/* rebuild full path for new format */
			if (dad && dad->parent) {
				strcpy(p, dad->full_name);
#ifdef DEBUG
				if ((strlen(p) + l + 1) != allocl) {
					DBG("%s: p: %d, l: %d, a: %d\n",
					    pathp, strlen(p), l, allocl);
				}
#endif
				p += strlen(p);
			}
			*(p++) = '/';
			memcpy(p, pathp, l);
		} else
			memcpy(np->full_name, pathp, l);
		prev_pp = &np->properties;
		**allnextpp = np;
		*allnextpp = &np->allnext;
		if (dad != NULL) {
			np->parent = dad;
			/* we temporarily use the next field as `last_child'*/
			if (dad->next == 0)
				dad->child = np;
			else
				dad->next->sibling = np;
			dad->next = np;
		}
		kref_init(&np->kref);
	}
	while(1) {
		u32 sz, noff;
		char *pname;

		tag = *((u32 *)(*p));
		if (tag == OF_DT_NOP) {
			*p += 4;
			continue;
		}
		if (tag != OF_DT_PROP)
			break;
		*p += 4;
		sz = *((u32 *)(*p));
		noff = *((u32 *)((*p) + 4));
		*p += 8;
		if (initial_boot_params->version < 0x10)
			*p = _ALIGN(*p, sz >= 8 ? 8 : 4);

		pname = find_flat_dt_string(noff);
		if (pname == NULL) {
			printk("Can't find property name in list !\n");
			break;
		}
		if (strcmp(pname, "name") == 0)
			has_name = 1;
		l = strlen(pname) + 1;
		pp = unflatten_dt_alloc(&mem, sizeof(struct property),
					__alignof__(struct property));
		if (allnextpp) {
			if (strcmp(pname, "linux,phandle") == 0) {
				np->node = *((u32 *)*p);
				if (np->linux_phandle == 0)
					np->linux_phandle = np->node;
			}
			if (strcmp(pname, "ibm,phandle") == 0)
				np->linux_phandle = *((u32 *)*p);
			pp->name = pname;
			pp->length = sz;
			pp->value = (void *)*p;
			*prev_pp = pp;
			prev_pp = &pp->next;
		}
		*p = _ALIGN((*p) + sz, 4);
	}
	/* with version 0x10 we may not have the name property, recreate
	 * it here from the unit name if absent
	 */
	if (!has_name) {
		char *p = pathp, *ps = pathp, *pa = NULL;
		int sz;

		while (*p) {
			if ((*p) == '@')
				pa = p;
			if ((*p) == '/')
				ps = p + 1;
			p++;
		}
		if (pa < ps)
			pa = p;
		sz = (pa - ps) + 1;
		pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
					__alignof__(struct property));
		if (allnextpp) {
			pp->name = "name";
			pp->length = sz;
			pp->value = (unsigned char *)(pp + 1);
			*prev_pp = pp;
			prev_pp = &pp->next;
			memcpy(pp->value, ps, sz - 1);
			((char *)pp->value)[sz - 1] = 0;
			DBG("fixed up name for %s -> %s\n", pathp, pp->value);
		}
	}
	if (allnextpp) {
		*prev_pp = NULL;
		np->name = get_property(np, "name", NULL);
		np->type = get_property(np, "device_type", NULL);

		if (!np->name)
			np->name = "<NULL>";
		if (!np->type)
			np->type = "<NULL>";
	}
	while (tag == OF_DT_BEGIN_NODE) {
		mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
		tag = *((u32 *)(*p));
	}
	if (tag != OF_DT_END_NODE) {
		printk("Weird tag at end of node: %x\n", tag);
		return mem;
	}
	*p += 4;
	return mem;
}


/**
 * unflattens the device-tree passed by the firmware, creating the
 * tree of struct device_node. It also fills the "name" and "type"
 * pointers of the nodes so the normal device-tree walking functions
 * can be used (this used to be done by finish_device_tree)
 */
void __init unflatten_device_tree(void)
{
	unsigned long start, mem, size;
	struct device_node **allnextp = &allnodes;
	char *p = NULL;
	int l = 0;

	DBG(" -> unflatten_device_tree()\n");

	/* First pass, scan for size */
	start = ((unsigned long)initial_boot_params) +
		initial_boot_params->off_dt_struct;
	size = unflatten_dt_node(0, &start, NULL, NULL, 0);
	size = (size | 3) + 1;

	DBG("  size is %lx, allocating...\n", size);

	/* Allocate memory for the expanded device tree */
	mem = lmb_alloc(size + 4, __alignof__(struct device_node));
	if (!mem) {
		DBG("Couldn't allocate memory with lmb_alloc()!\n");
		panic("Couldn't allocate memory with lmb_alloc()!\n");
	}
	mem = (unsigned long) __va(mem);

	((u32 *)mem)[size / 4] = 0xdeadbeef;

	DBG("  unflattening %lx...\n", mem);

	/* Second pass, do actual unflattening */
	start = ((unsigned long)initial_boot_params) +
		initial_boot_params->off_dt_struct;
	unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
	if (*((u32 *)start) != OF_DT_END)
		printk(KERN_WARNING "Weird tag at end of tree: %08x\n", *((u32 *)start));
	if (((u32 *)mem)[size / 4] != 0xdeadbeef)
		printk(KERN_WARNING "End of tree marker overwritten: %08x\n",
		       ((u32 *)mem)[size / 4] );
	*allnextp = NULL;

	/* Get pointer to OF "/chosen" node for use everywhere */
	of_chosen = of_find_node_by_path("/chosen");
	if (of_chosen == NULL)
		of_chosen = of_find_node_by_path("/chosen@0");

	/* Retreive command line */
	if (of_chosen != NULL) {
		p = (char *)get_property(of_chosen, "bootargs", &l);
		if (p != NULL && l > 0)
			strlcpy(cmd_line, p, min(l, COMMAND_LINE_SIZE));
	}
#ifdef CONFIG_CMDLINE
	if (l == 0 || (l == 1 && (*p) == 0))
		strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#endif /* CONFIG_CMDLINE */

	DBG("Command line is: %s\n", cmd_line);

	DBG(" <- unflatten_device_tree()\n");
}


static int __init early_init_dt_scan_cpus(unsigned long node,
					  const char *uname, int depth, void *data)
{
	u32 *prop;
	unsigned long size;
	char *type = of_get_flat_dt_prop(node, "device_type", &size);

	/* We are scanning "cpu" nodes only */
	if (type == NULL || strcmp(type, "cpu") != 0)
		return 0;

	boot_cpuid = 0;
	boot_cpuid_phys = 0;
	if (initial_boot_params && initial_boot_params->version >= 2) {
		/* version 2 of the kexec param format adds the phys cpuid
		 * of booted proc.
		 */
		boot_cpuid_phys = initial_boot_params->boot_cpuid_phys;
	} else {
		/* Check if it's the boot-cpu, set it's hw index now */
		if (of_get_flat_dt_prop(node,
					"linux,boot-cpu", NULL) != NULL) {
			prop = of_get_flat_dt_prop(node, "reg", NULL);
			if (prop != NULL)
				boot_cpuid_phys = *prop;
		}
	}
	set_hard_smp_processor_id(0, boot_cpuid_phys);

#ifdef CONFIG_ALTIVEC
	/* Check if we have a VMX and eventually update CPU features */
	prop = (u32 *)of_get_flat_dt_prop(node, "ibm,vmx", NULL);
	if (prop && (*prop) > 0) {
		cur_cpu_spec->cpu_features |= CPU_FTR_ALTIVEC;
		cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_ALTIVEC;
	}

	/* Same goes for Apple's "altivec" property */
	prop = (u32 *)of_get_flat_dt_prop(node, "altivec", NULL);
	if (prop) {
		cur_cpu_spec->cpu_features |= CPU_FTR_ALTIVEC;
		cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_ALTIVEC;
	}
#endif /* CONFIG_ALTIVEC */

#ifdef CONFIG_PPC_PSERIES
	/*
	 * Check for an SMT capable CPU and set the CPU feature. We do
	 * this by looking at the size of the ibm,ppc-interrupt-server#s
	 * property
	 */
	prop = (u32 *)of_get_flat_dt_prop(node, "ibm,ppc-interrupt-server#s",
				       &size);
	cur_cpu_spec->cpu_features &= ~CPU_FTR_SMT;
	if (prop && ((size / sizeof(u32)) > 1))
		cur_cpu_spec->cpu_features |= CPU_FTR_SMT;
#endif

	return 0;
}

static int __init early_init_dt_scan_chosen(unsigned long node,
					    const char *uname, int depth, void *data)
{
	u32 *prop;
	unsigned long *lprop;

	DBG("search \"chosen\", depth: %d, uname: %s\n", depth, uname);

	if (depth != 1 ||
	    (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
		return 0;

	/* get platform type */
	prop = (u32 *)of_get_flat_dt_prop(node, "linux,platform", NULL);
	if (prop == NULL)
		return 0;
#ifdef CONFIG_PPC_MULTIPLATFORM
	_machine = *prop;
#endif

#ifdef CONFIG_PPC64
	/* check if iommu is forced on or off */
	if (of_get_flat_dt_prop(node, "linux,iommu-off", NULL) != NULL)
		iommu_is_off = 1;
	if (of_get_flat_dt_prop(node, "linux,iommu-force-on", NULL) != NULL)
		iommu_force_on = 1;
#endif

 	lprop = of_get_flat_dt_prop(node, "linux,memory-limit", NULL);
 	if (lprop)
 		memory_limit = *lprop;

#ifdef CONFIG_PPC64
 	lprop = of_get_flat_dt_prop(node, "linux,tce-alloc-start", NULL);
 	if (lprop)
 		tce_alloc_start = *lprop;
 	lprop = of_get_flat_dt_prop(node, "linux,tce-alloc-end", NULL);
 	if (lprop)
 		tce_alloc_end = *lprop;
#endif

#ifdef CONFIG_PPC_RTAS
	/* To help early debugging via the front panel, we retreive a minimal
	 * set of RTAS infos now if available
	 */
	{
		u64 *basep, *entryp;

		basep = of_get_flat_dt_prop(node, "linux,rtas-base", NULL);
		entryp = of_get_flat_dt_prop(node, "linux,rtas-entry", NULL);
		prop = of_get_flat_dt_prop(node, "linux,rtas-size", NULL);
		if (basep && entryp && prop) {
			rtas.base = *basep;
			rtas.entry = *entryp;
			rtas.size = *prop;
		}
	}
#endif /* CONFIG_PPC_RTAS */

	/* break now */
	return 1;
}

static int __init early_init_dt_scan_root(unsigned long node,
					  const char *uname, int depth, void *data)
{
	u32 *prop;

	if (depth != 0)
		return 0;

	prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
	dt_root_size_cells = (prop == NULL) ? 1 : *prop;
	DBG("dt_root_size_cells = %x\n", dt_root_size_cells);

	prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
	dt_root_addr_cells = (prop == NULL) ? 2 : *prop;
	DBG("dt_root_addr_cells = %x\n", dt_root_addr_cells);
	
	/* break now */
	return 1;
}

static unsigned long __init dt_mem_next_cell(int s, cell_t **cellp)
{
	cell_t *p = *cellp;
	unsigned long r;

	/* Ignore more than 2 cells */
	while (s > sizeof(unsigned long) / 4) {
		p++;
		s--;
	}
	r = *p++;
#ifdef CONFIG_PPC64
	if (s > 1) {
		r <<= 32;
		r |= *(p++);
		s--;
	}
#endif

	*cellp = p;
	return r;
}


static int __init early_init_dt_scan_memory(unsigned long node,
					    const char *uname, int depth, void *data)
{
	char *type = of_get_flat_dt_prop(node, "device_type", NULL);
	cell_t *reg, *endp;
	unsigned long l;

	/* We are scanning "memory" nodes only */
	if (type == NULL) {
		/*
		 * The longtrail doesn't have a device_type on the
		 * /memory node, so look for the node called /memory@0.
		 */
		if (depth != 1 || strcmp(uname, "memory@0") != 0)
			return 0;
	} else if (strcmp(type, "memory") != 0)
		return 0;

	reg = (cell_t *)of_get_flat_dt_prop(node, "reg", &l);
	if (reg == NULL)
		return 0;

	endp = reg + (l / sizeof(cell_t));

	DBG("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
	    uname, l, reg[0], reg[1], reg[2], reg[3]);

	while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
		unsigned long base, size;

		base = dt_mem_next_cell(dt_root_addr_cells, &reg);
		size = dt_mem_next_cell(dt_root_size_cells, &reg);

		if (size == 0)
			continue;
		DBG(" - %lx ,  %lx\n", base, size);
#ifdef CONFIG_PPC64
		if (iommu_is_off) {
			if (base >= 0x80000000ul)
				continue;
			if ((base + size) > 0x80000000ul)
				size = 0x80000000ul - base;
		}
#endif
		lmb_add(base, size);
	}
	return 0;
}

static void __init early_reserve_mem(void)
{
	unsigned long base, size;
	unsigned long *reserve_map;

	reserve_map = (unsigned long *)(((unsigned long)initial_boot_params) +
					initial_boot_params->off_mem_rsvmap);
	while (1) {
		base = *(reserve_map++);
		size = *(reserve_map++);
		if (size == 0)
			break;
		DBG("reserving: %lx -> %lx\n", base, size);
		lmb_reserve(base, size);
	}

#if 0
	DBG("memory reserved, lmbs :\n");
      	lmb_dump_all();
#endif
}

void __init early_init_devtree(void *params)
{
	DBG(" -> early_init_devtree()\n");

	/* Setup flat device-tree pointer */
	initial_boot_params = params;

	/* Retrieve various informations from the /chosen node of the
	 * device-tree, including the platform type, initrd location and
	 * size, TCE reserve, and more ...
	 */
	of_scan_flat_dt(early_init_dt_scan_chosen, NULL);

	/* Scan memory nodes and rebuild LMBs */
	lmb_init();
	of_scan_flat_dt(early_init_dt_scan_root, NULL);
	of_scan_flat_dt(early_init_dt_scan_memory, NULL);
	lmb_enforce_memory_limit(memory_limit);
	lmb_analyze();
	lmb_reserve(0, __pa(klimit));

	DBG("Phys. mem: %lx\n", lmb_phys_mem_size());

	/* Reserve LMB regions used by kernel, initrd, dt, etc... */
	early_reserve_mem();

	DBG("Scanning CPUs ...\n");

	/* Retreive CPU related informations from the flat tree
	 * (altivec support, boot CPU ID, ...)
	 */
	of_scan_flat_dt(early_init_dt_scan_cpus, NULL);

	DBG(" <- early_init_devtree()\n");
}

#undef printk

int
prom_n_addr_cells(struct device_node* np)
{
	int* ip;
	do {
		if (np->parent)
			np = np->parent;
		ip = (int *) get_property(np, "#address-cells", NULL);
		if (ip != NULL)
			return *ip;
	} while (np->parent);
	/* No #address-cells property for the root node, default to 1 */
	return 1;
}

int
prom_n_size_cells(struct device_node* np)
{
	int* ip;
	do {
		if (np->parent)
			np = np->parent;
		ip = (int *) get_property(np, "#size-cells", NULL);
		if (ip != NULL)
			return *ip;
	} while (np->parent);
	/* No #size-cells property for the root node, default to 1 */
	return 1;
}

/**
 * Work out the sense (active-low level / active-high edge)
 * of each interrupt from the device tree.
 */
void __init prom_get_irq_senses(unsigned char *senses, int off, int max)
{
	struct device_node *np;
	int i, j;

	/* default to level-triggered */
	memset(senses, IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE, max - off);

	for (np = allnodes; np != 0; np = np->allnext) {
		for (j = 0; j < np->n_intrs; j++) {
			i = np->intrs[j].line;
			if (i >= off && i < max)
				senses[i-off] = np->intrs[j].sense;
		}
	}
}

/**
 * Construct and return a list of the device_nodes with a given name.
 */
struct device_node *find_devices(const char *name)
{
	struct device_node *head, **prevp, *np;

	prevp = &head;
	for (np = allnodes; np != 0; np = np->allnext) {
		if (np->name != 0 && strcasecmp(np->name, name) == 0) {
			*prevp = np;
			prevp = &np->next;
		}
	}
	*prevp = NULL;
	return head;
}
EXPORT_SYMBOL(find_devices);

/**
 * Construct and return a list of the device_nodes with a given type.
 */
struct device_node *find_type_devices(const char *type)
{
	struct device_node *head, **prevp, *np;

	prevp = &head;
	for (np = allnodes; np != 0; np = np->allnext) {
		if (np->type != 0 && strcasecmp(np->type, type) == 0) {
			*prevp = np;
			prevp = &np->next;
		}
	}
	*prevp = NULL;
	return head;
}
EXPORT_SYMBOL(find_type_devices);

/**
 * Returns all nodes linked together
 */
struct device_node *find_all_nodes(void)
{
	struct device_node *head, **prevp, *np;

	prevp = &head;
	for (np = allnodes; np != 0; np = np->allnext) {
		*prevp = np;
		prevp = &np->next;
	}
	*prevp = NULL;
	return head;
}
EXPORT_SYMBOL(find_all_nodes);

/** Checks if the given "compat" string matches one of the strings in
 * the device's "compatible" property
 */
int device_is_compatible(struct device_node *device, const char *compat)
{
	const char* cp;
	int cplen, l;

	cp = (char *) get_property(device, "compatible", &cplen);
	if (cp == NULL)
		return 0;
	while (cplen > 0) {
		if (strncasecmp(cp, compat, strlen(compat)) == 0)
			return 1;
		l = strlen(cp) + 1;
		cp += l;
		cplen -= l;
	}

	return 0;
}
EXPORT_SYMBOL(device_is_compatible);


/**
 * Indicates whether the root node has a given value in its
 * compatible property.
 */
int machine_is_compatible(const char *compat)
{
	struct device_node *root;
	int rc = 0;

	root = of_find_node_by_path("/");
	if (root) {
		rc = device_is_compatible(root, compat);
		of_node_put(root);
	}
	return rc;
}
EXPORT_SYMBOL(machine_is_compatible);

/**
 * Construct and return a list of the device_nodes with a given type
 * and compatible property.
 */
struct device_node *find_compatible_devices(const char *type,
					    const char *compat)
{
	struct device_node *head, **prevp, *np;

	prevp = &head;
	for (np = allnodes; np != 0; np = np->allnext) {
		if (type != NULL
		    && !(np->type != 0 && strcasecmp(np->type, type) == 0))
			continue;
		if (device_is_compatible(np, compat)) {
			*prevp = np;
			prevp = &np->next;
		}
	}
	*prevp = NULL;
	return head;
}
EXPORT_SYMBOL(find_compatible_devices);

/**
 * Find the device_node with a given full_name.
 */
struct device_node *find_path_device(const char *path)
{
	struct device_node *np;

	for (np = allnodes; np != 0; np = np->allnext)
		if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0)
			return np;
	return NULL;
}
EXPORT_SYMBOL(find_path_device);

/*******
 *
 * New implementation of the OF "find" APIs, return a refcounted
 * object, call of_node_put() when done.  The device tree and list
 * are protected by a rw_lock.
 *
 * Note that property management will need some locking as well,
 * this isn't dealt with yet.
 *
 *******/

/**
 *	of_find_node_by_name - Find a node by its "name" property
 *	@from:	The node to start searching from or NULL, the node
 *		you pass will not be searched, only the next one
 *		will; typically, you pass what the previous call
 *		returned. of_node_put() will be called on it
 *	@name:	The name string to match against
 *
 *	Returns a node pointer with refcount incremented, use
 *	of_node_put() on it when done.
 */
struct device_node *of_find_node_by_name(struct device_node *from,
	const char *name)
{
	struct device_node *np;

	read_lock(&devtree_lock);
	np = from ? from->allnext : allnodes;
	for (; np != 0; np = np->allnext)
		if (np->name != 0 && strcasecmp(np->name, name) == 0
		    && of_node_get(np))
			break;
	if (from)
		of_node_put(from);
	read_unlock(&devtree_lock);
	return np;
}
EXPORT_SYMBOL(of_find_node_by_name);

/**
 *	of_find_node_by_type - Find a node by its "device_type" property
 *	@from:	The node to start searching from or NULL, the node
 *		you pass will not be searched, only the next one
 *		will; typically, you pass what the previous call
 *		returned. of_node_put() will be called on it
 *	@name:	The type string to match against
 *
 *	Returns a node pointer with refcount incremented, use
 *	of_node_put() on it when done.
 */
struct device_node *of_find_node_by_type(struct device_node *from,
	const char *type)
{
	struct device_node *np;

	read_lock(&devtree_lock);
	np = from ? from->allnext : allnodes;
	for (; np != 0; np = np->allnext)
		if (np->type != 0 && strcasecmp(np->type, type) == 0
		    && of_node_get(np))
			break;
	if (from)
		of_node_put(from);
	read_unlock(&devtree_lock);
	return np;
}
EXPORT_SYMBOL(of_find_node_by_type);

/**
 *	of_find_compatible_node - Find a node based on type and one of the
 *                                tokens in its "compatible" property
 *	@from:		The node to start searching from or NULL, the node
 *			you pass will not be searched, only the next one
 *			will; typically, you pass what the previous call
 *			returned. of_node_put() will be called on it
 *	@type:		The type string to match "device_type" or NULL to ignore
 *	@compatible:	The string to match to one of the tokens in the device
 *			"compatible" list.
 *
 *	Returns a node pointer with refcount incremented, use
 *	of_node_put() on it when done.
 */
struct device_node *of_find_compatible_node(struct device_node *from,
	const char *type, const char *compatible)
{
	struct device_node *np;

	read_lock(&devtree_lock);
	np = from ? from->allnext : allnodes;
	for (; np != 0; np = np->allnext) {
		if (type != NULL
		    && !(np->type != 0 && strcasecmp(np->type, type) == 0))
			continue;
		if (device_is_compatible(np, compatible) && of_node_get(np))
			break;
	}
	if (from)
		of_node_put(from);
	read_unlock(&devtree_lock);
	return np;
}
EXPORT_SYMBOL(of_find_compatible_node);

/**
 *	of_find_node_by_path - Find a node matching a full OF path
 *	@path:	The full path to match
 *
 *	Returns a node pointer with refcount incremented, use
 *	of_node_put() on it when done.
 */
struct device_node *of_find_node_by_path(const char *path)
{
	struct device_node *np = allnodes;

	read_lock(&devtree_lock);
	for (; np != 0; np = np->allnext) {
		if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0
		    && of_node_get(np))
			break;
	}
	read_unlock(&devtree_lock);
	return np;
}
EXPORT_SYMBOL(of_find_node_by_path);

/**
 *	of_find_node_by_phandle - Find a node given a phandle
 *	@handle:	phandle of the node to find
 *
 *	Returns a node pointer with refcount incremented, use
 *	of_node_put() on it when done.
 */
struct device_node *of_find_node_by_phandle(phandle handle)
{
	struct device_node *np;

	read_lock(&devtree_lock);
	for (np = allnodes; np != 0; np = np->allnext)
		if (np->linux_phandle == handle)
			break;
	if (np)
		of_node_get(np);
	read_unlock(&devtree_lock);
	return np;
}
EXPORT_SYMBOL(of_find_node_by_phandle);

/**
 *	of_find_all_nodes - Get next node in global list
 *	@prev:	Previous node or NULL to start iteration
 *		of_node_put() will be called on it
 *
 *	Returns a node pointer with refcount incremented, use
 *	of_node_put() on it when done.
 */
struct device_node *of_find_all_nodes(struct device_node *prev)
{
	struct device_node *np;

	read_lock(&devtree_lock);
	np = prev ? prev->allnext : allnodes;
	for (; np != 0; np = np->allnext)
		if (of_node_get(np))
			break;
	if (prev)
		of_node_put(prev);
	read_unlock(&devtree_lock);
	return np;
}
EXPORT_SYMBOL(of_find_all_nodes);

/**
 *	of_get_parent - Get a node's parent if any
 *	@node:	Node to get parent
 *
 *	Returns a node pointer with refcount incremented, use
 *	of_node_put() on it when done.
 */
struct device_node *of_get_parent(const struct device_node *node)
{
	struct device_node *np;

	if (!node)
		return NULL;

	read_lock(&devtree_lock);
	np = of_node_get(node->parent);
	read_unlock(&devtree_lock);
	return np;
}
EXPORT_SYMBOL(of_get_parent);

/**
 *	of_get_next_child - Iterate a node childs
 *	@node:	parent node
 *	@prev:	previous child of the parent node, or NULL to get first
 *
 *	Returns a node pointer with refcount incremented, use
 *	of_node_put() on it when done.
 */
struct device_node *of_get_next_child(const struct device_node *node,
	struct device_node *prev)
{
	struct device_node *next;

	read_lock(&devtree_lock);
	next = prev ? prev->sibling : node->child;
	for (; next != 0; next = next->sibling)
		if (of_node_get(next))
			break;
	if (prev)
		of_node_put(prev);
	read_unlock(&devtree_lock);
	return next;
}
EXPORT_SYMBOL(of_get_next_child);

/**
 *	of_node_get - Increment refcount of a node
 *	@node:	Node to inc refcount, NULL is supported to
 *		simplify writing of callers
 *
 *	Returns node.
 */
struct device_node *of_node_get(struct device_node *node)
{
	if (node)
		kref_get(&node->kref);
	return node;
}
EXPORT_SYMBOL(of_node_get);

static inline struct device_node * kref_to_device_node(struct kref *kref)
{
	return container_of(kref, struct device_node, kref);
}

/**
 *	of_node_release - release a dynamically allocated node
 *	@kref:  kref element of the node to be released
 *
 *	In of_node_put() this function is passed to kref_put()
 *	as the destructor.
 */
static void of_node_release(struct kref *kref)
{
	struct device_node *node = kref_to_device_node(kref);
	struct property *prop = node->properties;

	if (!OF_IS_DYNAMIC(node))
		return;
	while (prop) {
		struct property *next = prop->next;
		kfree(prop->name);
		kfree(prop->value);
		kfree(prop);
		prop = next;
	}
	kfree(node->intrs);
	kfree(node->addrs);
	kfree(node->full_name);
	kfree(node->data);
	kfree(node);
}

/**
 *	of_node_put - Decrement refcount of a node
 *	@node:	Node to dec refcount, NULL is supported to
 *		simplify writing of callers
 *
 */
void of_node_put(struct device_node *node)
{
	if (node)
		kref_put(&node->kref, of_node_release);
}
EXPORT_SYMBOL(of_node_put);

/*
 * Plug a device node into the tree and global list.
 */
void of_attach_node(struct device_node *np)
{
	write_lock(&devtree_lock);
	np->sibling = np->parent->child;
	np->allnext = allnodes;
	np->parent->child = np;
	allnodes = np;
	write_unlock(&devtree_lock);
}

/*
 * "Unplug" a node from the device tree.  The caller must hold
 * a reference to the node.  The memory associated with the node
 * is not freed until its refcount goes to zero.
 */
void of_detach_node(const struct device_node *np)
{
	struct device_node *parent;

	write_lock(&devtree_lock);

	parent = np->parent;

	if (allnodes == np)
		allnodes = np->allnext;
	else {
		struct device_node *prev;
		for (prev = allnodes;
		     prev->allnext != np;
		     prev = prev->allnext)
			;
		prev->allnext = np->allnext;
	}

	if (parent->child == np)
		parent->child = np->sibling;
	else {
		struct device_node *prevsib;
		for (prevsib = np->parent->child;
		     prevsib->sibling != np;
		     prevsib = prevsib->sibling)
			;
		prevsib->sibling = np->sibling;
	}

	write_unlock(&devtree_lock);
}

#ifdef CONFIG_PPC_PSERIES
/*
 * Fix up the uninitialized fields in a new device node:
 * name, type, n_addrs, addrs, n_intrs, intrs, and pci-specific fields
 *
 * A lot of boot-time code is duplicated here, because functions such
 * as finish_node_interrupts, interpret_pci_props, etc. cannot use the
 * slab allocator.
 *
 * This should probably be split up into smaller chunks.
 */

static int of_finish_dynamic_node(struct device_node *node,
				  unsigned long *unused1, int unused2,
				  int unused3, int unused4)
{
	struct device_node *parent = of_get_parent(node);
	int err = 0;
	phandle *ibm_phandle;

	node->name = get_property(node, "name", NULL);
	node->type = get_property(node, "device_type", NULL);

	if (!parent) {
		err = -ENODEV;
		goto out;
	}

	/* We don't support that function on PowerMac, at least
	 * not yet
	 */
	if (_machine == PLATFORM_POWERMAC)
		return -ENODEV;

	/* fix up new node's linux_phandle field */
	if ((ibm_phandle = (unsigned int *)get_property(node, "ibm,phandle", NULL)))
		node->linux_phandle = *ibm_phandle;

out:
	of_node_put(parent);
	return err;
}

static int prom_reconfig_notifier(struct notifier_block *nb,
				  unsigned long action, void *node)
{
	int err;

	switch (action) {
	case PSERIES_RECONFIG_ADD:
		err = finish_node(node, NULL, of_finish_dynamic_node, 0, 0, 0);
		if (err < 0) {
			printk(KERN_ERR "finish_node returned %d\n", err);
			err = NOTIFY_BAD;
		}
		break;
	default:
		err = NOTIFY_DONE;
		break;
	}
	return err;
}

static struct notifier_block prom_reconfig_nb = {
	.notifier_call = prom_reconfig_notifier,
	.priority = 10, /* This one needs to run first */
};

static int __init prom_reconfig_setup(void)
{
	return pSeries_reconfig_notifier_register(&prom_reconfig_nb);
}
__initcall(prom_reconfig_setup);
#endif

/*
 * Find a property with a given name for a given node
 * and return the value.
 */
unsigned char *get_property(struct device_node *np, const char *name,
			    int *lenp)
{
	struct property *pp;

	for (pp = np->properties; pp != 0; pp = pp->next)
		if (strcmp(pp->name, name) == 0) {
			if (lenp != 0)
				*lenp = pp->length;
			return pp->value;
		}
	return NULL;
}
EXPORT_SYMBOL(get_property);

/*
 * Add a property to a node
 */
int prom_add_property(struct device_node* np, struct property* prop)
{
	struct property **next;

	prop->next = NULL;	
	write_lock(&devtree_lock);
	next = &np->properties;
	while (*next) {
		if (strcmp(prop->name, (*next)->name) == 0) {
			/* duplicate ! don't insert it */
			write_unlock(&devtree_lock);
			return -1;
		}
		next = &(*next)->next;
	}
	*next = prop;
	write_unlock(&devtree_lock);

#ifdef CONFIG_PROC_DEVICETREE
	/* try to add to proc as well if it was initialized */
	if (np->pde)
		proc_device_tree_add_prop(np->pde, prop);
#endif /* CONFIG_PROC_DEVICETREE */

	return 0;
}

/* I quickly hacked that one, check against spec ! */
static inline unsigned long
bus_space_to_resource_flags(unsigned int bus_space)
{
	u8 space = (bus_space >> 24) & 0xf;
	if (space == 0)
		space = 0x02;
	if (space == 0x02)
		return IORESOURCE_MEM;
	else if (space == 0x01)
		return IORESOURCE_IO;
	else {
		printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n",
		    	bus_space);
		return 0;
	}
}

#ifdef CONFIG_PCI
static struct resource *find_parent_pci_resource(struct pci_dev* pdev,
						 struct address_range *range)
{
	unsigned long mask;
	int i;

	/* Check this one */
	mask = bus_space_to_resource_flags(range->space);
	for (i=0; i<DEVICE_COUNT_RESOURCE; i++) {
		if ((pdev->resource[i].flags & mask) == mask &&
			pdev->resource[i].start <= range->address &&
			pdev->resource[i].end > range->address) {
				if ((range->address + range->size - 1) > pdev->resource[i].end) {
					/* Add better message */
					printk(KERN_WARNING "PCI/OF resource overlap !\n");
					return NULL;
				}
				break;
			}
	}
	if (i == DEVICE_COUNT_RESOURCE)
		return NULL;
	return &pdev->resource[i];
}

/*
 * Request an OF device resource. Currently handles child of PCI devices,
 * or other nodes attached to the root node. Ultimately, put some
 * link to resources in the OF node.
 */
struct resource *request_OF_resource(struct device_node* node, int index,
				     const char* name_postfix)
{
	struct pci_dev* pcidev;
	u8 pci_bus, pci_devfn;
	unsigned long iomask;
	struct device_node* nd;
	struct resource* parent;
	struct resource *res = NULL;
	int nlen, plen;

	if (index >= node->n_addrs)
		goto fail;

	/* Sanity check on bus space */
	iomask = bus_space_to_resource_flags(node->addrs[index].space);
	if (iomask & IORESOURCE_MEM)
		parent = &iomem_resource;
	else if (iomask & IORESOURCE_IO)
		parent = &ioport_resource;
	else
		goto fail;

	/* Find a PCI parent if any */
	nd = node;
	pcidev = NULL;
	while (nd) {
		if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
			pcidev = pci_find_slot(pci_bus, pci_devfn);
		if (pcidev) break;
		nd = nd->parent;
	}
	if (pcidev)
		parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
	if (!parent) {
		printk(KERN_WARNING "request_OF_resource(%s), parent not found\n",
			node->name);
		goto fail;
	}

	res = __request_region(parent, node->addrs[index].address,
			       node->addrs[index].size, NULL);
	if (!res)
		goto fail;
	nlen = strlen(node->name);
	plen = name_postfix ? strlen(name_postfix) : 0;
	res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL);
	if (res->name) {
		strcpy((char *)res->name, node->name);
		if (plen)
			strcpy((char *)res->name+nlen, name_postfix);
	}
	return res;
fail:
	return NULL;
}
EXPORT_SYMBOL(request_OF_resource);

int release_OF_resource(struct device_node *node, int index)
{
	struct pci_dev* pcidev;
	u8 pci_bus, pci_devfn;
	unsigned long iomask, start, end;
	struct device_node* nd;
	struct resource* parent;
	struct resource *res = NULL;

	if (index >= node->n_addrs)
		return -EINVAL;

	/* Sanity check on bus space */
	iomask = bus_space_to_resource_flags(node->addrs[index].space);
	if (iomask & IORESOURCE_MEM)
		parent = &iomem_resource;
	else if (iomask & IORESOURCE_IO)
		parent = &ioport_resource;
	else
		return -EINVAL;

	/* Find a PCI parent if any */
	nd = node;
	pcidev = NULL;
	while(nd) {
		if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
			pcidev = pci_find_slot(pci_bus, pci_devfn);
		if (pcidev) break;
		nd = nd->parent;
	}
	if (pcidev)
		parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
	if (!parent) {
		printk(KERN_WARNING "release_OF_resource(%s), parent not found\n",
			node->name);
		return -ENODEV;
	}

	/* Find us in the parent and its childs */
	res = parent->child;
	start = node->addrs[index].address;
	end = start + node->addrs[index].size - 1;
	while (res) {
		if (res->start == start && res->end == end &&
		    (res->flags & IORESOURCE_BUSY))
		    	break;
		if (res->start <= start && res->end >= end)
			res = res->child;
		else
			res = res->sibling;
	}
	if (!res)
		return -ENODEV;

	if (res->name) {
		kfree(res->name);
		res->name = NULL;
	}
	release_resource(res);
	kfree(res);

	return 0;
}
EXPORT_SYMBOL(release_OF_resource);
#endif /* CONFIG_PCI */