diff options
Diffstat (limited to 'arch/sparc64')
57 files changed, 4100 insertions, 3481 deletions
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index 43a66f5407f..a7a111db25b 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -87,6 +87,10 @@ config SYSVIPC_COMPAT depends on COMPAT && SYSVIPC default y +config GENERIC_HARDIRQS + bool + default y + menu "General machine setup" config SMP diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index f09a70b8aab..b2f41147d0e 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.17-rc3 -# Fri May 12 12:43:49 2006 +# Linux kernel version: 2.6.17 +# Fri Jun 23 23:17:09 2006 # CONFIG_SPARC=y CONFIG_SPARC64=y @@ -87,6 +87,7 @@ CONFIG_DEFAULT_AS=y # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="anticipatory" CONFIG_SYSVIPC_COMPAT=y +CONFIG_GENERIC_HARDIRQS=y # # General machine setup @@ -183,6 +184,8 @@ CONFIG_INET_ESP=y CONFIG_INET_IPCOMP=y CONFIG_INET_XFRM_TUNNEL=y CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y CONFIG_TCP_CONG_ADVANCED=y @@ -198,6 +201,9 @@ CONFIG_TCP_CONG_HSTCP=m CONFIG_TCP_CONG_HYBLA=m CONFIG_TCP_CONG_VEGAS=m CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_COMPOUND=m CONFIG_IPV6=m CONFIG_IPV6_PRIVACY=y CONFIG_IPV6_ROUTER_PREF=y @@ -207,7 +213,10 @@ CONFIG_INET6_ESP=m CONFIG_INET6_IPCOMP=m CONFIG_INET6_XFRM_TUNNEL=m CONFIG_INET6_TUNNEL=m +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m CONFIG_IPV6_TUNNEL=m +# CONFIG_NETWORK_SECMARK is not set # CONFIG_NETFILTER is not set # @@ -260,6 +269,7 @@ CONFIG_VLAN_8021Q=m # Network testing # CONFIG_NET_PKTGEN=m +CONFIG_NET_TCPPROBE=m # CONFIG_HAMRADIO is not set # CONFIG_IRDA is not set # CONFIG_BT is not set @@ -276,6 +286,7 @@ CONFIG_STANDALONE=y # CONFIG_PREVENT_FIRMWARE_BUILD is not set CONFIG_FW_LOADER=y # CONFIG_DEBUG_DRIVER is not set +# CONFIG_SYS_HYPERVISOR is not set # # Connector - unified userspace <-> kernelspace linker @@ -424,6 +435,7 @@ CONFIG_ISCSI_TCP=m # CONFIG_MEGARAID_LEGACY is not set # CONFIG_MEGARAID_SAS is not set # CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_HPTIOP is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_IPS is not set @@ -562,6 +574,7 @@ CONFIG_BNX2=m # CONFIG_CHELSIO_T1 is not set # CONFIG_IXGB is not set # CONFIG_S2IO is not set +# CONFIG_MYRI10GE is not set # # Token Ring devices @@ -722,6 +735,7 @@ CONFIG_I2C_ALGOBIT=y # CONFIG_I2C_I810 is not set # CONFIG_I2C_PIIX4 is not set # CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_OCORES is not set # CONFIG_I2C_PARPORT_LIGHT is not set # CONFIG_I2C_PROSAVAGE is not set # CONFIG_I2C_SAVAGE4 is not set @@ -765,6 +779,7 @@ CONFIG_I2C_ALGOBIT=y # CONFIG_HWMON=y # CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_ABITUGURU is not set # CONFIG_SENSORS_ADM1021 is not set # CONFIG_SENSORS_ADM1025 is not set # CONFIG_SENSORS_ADM1026 is not set @@ -793,10 +808,12 @@ CONFIG_HWMON=y # CONFIG_SENSORS_PC87360 is not set # CONFIG_SENSORS_SIS5595 is not set # CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set # CONFIG_SENSORS_SMSC47B397 is not set # CONFIG_SENSORS_VIA686A is not set # CONFIG_SENSORS_VT8231 is not set # CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set # CONFIG_SENSORS_W83792D is not set # CONFIG_SENSORS_W83L785TS is not set # CONFIG_SENSORS_W83627HF is not set @@ -811,6 +828,7 @@ CONFIG_HWMON=y # Multimedia devices # # CONFIG_VIDEO_DEV is not set +CONFIG_VIDEO_V4L2=y # # Digital Video Broadcasting Devices @@ -1006,6 +1024,7 @@ CONFIG_USB_DEVICEFS=y CONFIG_USB_EHCI_HCD=m # CONFIG_USB_EHCI_SPLIT_ISO is not set # CONFIG_USB_EHCI_ROOT_HUB_TT is not set +# CONFIG_USB_EHCI_TT_NEWSCHED is not set # CONFIG_USB_ISP116X_HCD is not set CONFIG_USB_OHCI_HCD=y # CONFIG_USB_OHCI_BIG_ENDIAN is not set @@ -1085,10 +1104,12 @@ CONFIG_USB_HIDDEV=y # CONFIG_USB_LEGOTOWER is not set # CONFIG_USB_LCD is not set # CONFIG_USB_LED is not set +# CONFIG_USB_CY7C63 is not set # CONFIG_USB_CYTHERM is not set # CONFIG_USB_PHIDGETKIT is not set # CONFIG_USB_PHIDGETSERVO is not set # CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_APPLEDISPLAY is not set # CONFIG_USB_SISUSBVGA is not set # CONFIG_USB_LD is not set # CONFIG_USB_TEST is not set @@ -1135,6 +1156,19 @@ CONFIG_USB_HIDDEV=y # CONFIG_RTC_CLASS is not set # +# DMA Engine support +# +# CONFIG_DMA_ENGINE is not set + +# +# DMA Clients +# + +# +# DMA Devices +# + +# # Misc Linux/SPARC drivers # CONFIG_SUN_OPENPROMIO=m @@ -1173,6 +1207,7 @@ CONFIG_FS_POSIX_ACL=y # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y # CONFIG_QUOTA is not set CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 6f6816488b0..86c9fe3f3e4 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -12,7 +12,7 @@ obj-y := process.o setup.o cpu.o idprom.o \ irq.o ptrace.o time.o sys_sparc.o signal.o \ unaligned.o central.o pci.o starfire.o semaphore.o \ power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \ - visemul.o + visemul.o prom.o of_device.o obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ pci_psycho.o pci_sabre.o pci_schizo.o \ diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c index 8852c20c8d9..718350aba1e 100644 --- a/arch/sparc64/kernel/auxio.c +++ b/arch/sparc64/kernel/auxio.c @@ -6,18 +6,18 @@ */ #include <linux/config.h> +#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/ioport.h> -#include <asm/oplib.h> +#include <asm/prom.h> +#include <asm/of_device.h> #include <asm/io.h> -#include <asm/sbus.h> -#include <asm/ebus.h> #include <asm/auxio.h> -/* This cannot be static, as it is referenced in irq.c */ void __iomem *auxio_register = NULL; +EXPORT_SYMBOL(auxio_register); enum auxio_type { AUXIO_TYPE_NODEV, @@ -110,43 +110,57 @@ void auxio_set_lte(int on) } } -void __init auxio_probe(void) +static struct of_device_id auxio_match[] = { + { + .name = "auxio", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, auxio_match); + +static int __devinit auxio_probe(struct of_device *dev, const struct of_device_id *match) { - struct sbus_bus *sbus; - struct sbus_dev *sdev = NULL; - - for_each_sbus(sbus) { - for_each_sbusdev(sdev, sbus) { - if(!strcmp(sdev->prom_name, "auxio")) - goto found_sdev; - } - } - -found_sdev: - if (sdev) { - auxio_devtype = AUXIO_TYPE_SBUS; - auxio_register = sbus_ioremap(&sdev->resource[0], 0, - sdev->reg_addrs[0].reg_size, - "auxiliaryIO"); - } -#ifdef CONFIG_PCI - else { - struct linux_ebus *ebus; - struct linux_ebus_device *edev = NULL; - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_name, "auxio")) - goto ebus_done; - } - } - ebus_done: - if (edev) { - auxio_devtype = AUXIO_TYPE_EBUS; - auxio_register = - ioremap(edev->resource[0].start, sizeof(u32)); - } + struct device_node *dp = dev->node; + unsigned long size; + + if (!strcmp(dp->parent->name, "ebus")) { + auxio_devtype = AUXIO_TYPE_EBUS; + size = sizeof(u32); + } else if (!strcmp(dp->parent->name, "sbus")) { + auxio_devtype = AUXIO_TYPE_SBUS; + size = 1; + } else { + printk("auxio: Unknown parent bus type [%s]\n", + dp->parent->name); + return -ENODEV; } - auxio_set_led(AUXIO_LED_ON); -#endif + auxio_register = of_ioremap(&dev->resource[0], 0, size, "auxio"); + if (!auxio_register) + return -ENODEV; + + printk(KERN_INFO "AUXIO: Found device at %s\n", + dp->full_name); + + if (auxio_devtype == AUXIO_TYPE_EBUS) + auxio_set_led(AUXIO_LED_ON); + + return 0; } + +static struct of_platform_driver auxio_driver = { + .name = "auxio", + .match_table = auxio_match, + .probe = auxio_probe, +}; + +static int __init auxio_init(void) +{ + return of_register_driver(&auxio_driver, &of_bus_type); +} + +/* Must be after subsys_initcall() so that busses are probed. Must + * be before device_initcall() because things like the floppy driver + * need to use the AUXIO register. + */ +fs_initcall(auxio_init); diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c index 8a2abcce273..a98f3ae175a 100644 --- a/arch/sparc64/kernel/binfmt_elf32.c +++ b/arch/sparc64/kernel/binfmt_elf32.c @@ -84,7 +84,6 @@ typedef struct { #include <asm/processor.h> #include <linux/module.h> -#include <linux/config.h> #include <linux/elfcore.h> #include <linux/compat.h> diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c index 3d184a78496..b66336db00e 100644 --- a/arch/sparc64/kernel/central.c +++ b/arch/sparc64/kernel/central.c @@ -29,28 +29,34 @@ static void central_probe_failure(int line) prom_halt(); } -static void central_ranges_init(int cnode, struct linux_central *central) +static void central_ranges_init(struct linux_central *central) { - int success; + struct device_node *dp = central->prom_node; + void *pval; + int len; central->num_central_ranges = 0; - success = prom_getproperty(central->prom_node, "ranges", - (char *) central->central_ranges, - sizeof (central->central_ranges)); - if (success != -1) - central->num_central_ranges = (success/sizeof(struct linux_prom_ranges)); + pval = of_get_property(dp, "ranges", &len); + if (pval) { + memcpy(central->central_ranges, pval, len); + central->num_central_ranges = + (len / sizeof(struct linux_prom_ranges)); + } } -static void fhc_ranges_init(int fnode, struct linux_fhc *fhc) +static void fhc_ranges_init(struct linux_fhc *fhc) { - int success; + struct device_node *dp = fhc->prom_node; + void *pval; + int len; fhc->num_fhc_ranges = 0; - success = prom_getproperty(fhc->prom_node, "ranges", - (char *) fhc->fhc_ranges, - sizeof (fhc->fhc_ranges)); - if (success != -1) - fhc->num_fhc_ranges = (success/sizeof(struct linux_prom_ranges)); + pval = of_get_property(dp, "ranges", &len); + if (pval) { + memcpy(fhc->fhc_ranges, pval, len); + fhc->num_fhc_ranges = + (len / sizeof(struct linux_prom_ranges)); + } } /* Range application routines are exported to various drivers, @@ -112,15 +118,10 @@ static unsigned long prom_reg_to_paddr(struct linux_prom_registers *r) static void probe_other_fhcs(void) { - struct linux_prom64_registers fpregs[6]; - char namebuf[128]; - int node; + struct device_node *dp; + struct linux_prom64_registers *fpregs; - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(node, "fhc"); - if (node == 0) - central_probe_failure(__LINE__); - while (node) { + for_each_node_by_name(dp, "fhc") { struct linux_fhc *fhc; int board; u32 tmp; @@ -137,14 +138,12 @@ static void probe_other_fhcs(void) /* Toplevel FHCs have no parent. */ fhc->parent = NULL; - fhc->prom_node = node; - prom_getstring(node, "name", namebuf, sizeof(namebuf)); - strcpy(fhc->prom_name, namebuf); - fhc_ranges_init(node, fhc); + fhc->prom_node = dp; + fhc_ranges_init(fhc); /* Non-central FHC's have 64-bit OBP format registers. */ - if (prom_getproperty(node, "reg", - (char *)&fpregs[0], sizeof(fpregs)) == -1) + fpregs = of_get_property(dp, "reg", NULL); + if (!fpregs) central_probe_failure(__LINE__); /* Only central FHC needs special ranges applied. */ @@ -155,7 +154,7 @@ static void probe_other_fhcs(void) fhc->fhc_regs.uregs = fpregs[4].phys_addr; fhc->fhc_regs.tregs = fpregs[5].phys_addr; - board = prom_getintdefault(node, "board#", -1); + board = of_getintprop_default(dp, "board#", -1); fhc->board = board; tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_JCTRL); @@ -179,33 +178,33 @@ static void probe_other_fhcs(void) tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); tmp |= FHC_CONTROL_IXIST; upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); - - /* Look for the next FHC. */ - node = prom_getsibling(node); - if (node == 0) - break; - node = prom_searchsiblings(node, "fhc"); - if (node == 0) - break; } } static void probe_clock_board(struct linux_central *central, struct linux_fhc *fhc, - int cnode, int fnode) + struct device_node *fp) { - struct linux_prom_registers cregs[3]; - int clknode, nslots, tmp, nregs; + struct device_node *dp; + struct linux_prom_registers cregs[3], *pr; + int nslots, tmp, nregs; - clknode = prom_searchsiblings(prom_getchild(fnode), "clock-board"); - if (clknode == 0 || clknode == -1) + dp = fp->child; + while (dp) { + if (!strcmp(dp->name, "clock-board")) + break; + dp = dp->sibling; + } + if (!dp) central_probe_failure(__LINE__); - nregs = prom_getproperty(clknode, "reg", (char *)&cregs[0], sizeof(cregs)); - if (nregs == -1) + pr = of_get_property(dp, "reg", &nregs); + if (!pr) central_probe_failure(__LINE__); + memcpy(cregs, pr, nregs); nregs /= sizeof(struct linux_prom_registers); + apply_fhc_ranges(fhc, &cregs[0], nregs); apply_central_ranges(central, &cregs[0], nregs); central->cfreg = prom_reg_to_paddr(&cregs[0]); @@ -296,13 +295,13 @@ static void init_all_fhc_hw(void) void central_probe(void) { - struct linux_prom_registers fpregs[6]; + struct linux_prom_registers fpregs[6], *pr; struct linux_fhc *fhc; - char namebuf[128]; - int cnode, fnode, err; + struct device_node *dp, *fp; + int err; - cnode = prom_finddevice("/central"); - if (cnode == 0 || cnode == -1) { + dp = of_find_node_by_name(NULL, "central"); + if (!dp) { if (this_is_starfire) starfire_cpu_setup(); return; @@ -321,31 +320,31 @@ void central_probe(void) /* First init central. */ central_bus->child = fhc; - central_bus->prom_node = cnode; - - prom_getstring(cnode, "name", namebuf, sizeof(namebuf)); - strcpy(central_bus->prom_name, namebuf); - - central_ranges_init(cnode, central_bus); + central_bus->prom_node = dp; + central_ranges_init(central_bus); /* And then central's FHC. */ fhc->next = fhc_list; fhc_list = fhc; fhc->parent = central_bus; - fnode = prom_searchsiblings(prom_getchild(cnode), "fhc"); - if (fnode == 0 || fnode == -1) + fp = dp->child; + while (fp) { + if (!strcmp(fp->name, "fhc")) + break; + fp = fp->sibling; + } + if (!fp) central_probe_failure(__LINE__); - fhc->prom_node = fnode; - prom_getstring(fnode, "name", namebuf, sizeof(namebuf)); - strcpy(fhc->prom_name, namebuf); - - fhc_ranges_init(fnode, fhc); + fhc->prom_node = fp; + fhc_ranges_init(fhc); /* Now, map in FHC register set. */ - if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1) + pr = of_get_property(fp, "reg", NULL); + if (!pr) central_probe_failure(__LINE__); + memcpy(fpregs, pr, sizeof(fpregs)); apply_central_ranges(central_bus, &fpregs[0], 6); @@ -366,7 +365,7 @@ void central_probe(void) fhc->jtag_master = 0; /* Attach the clock board registers for CENTRAL. */ - probe_clock_board(central_bus, fhc, cnode, fnode); + probe_clock_board(central_bus, fhc, fp); err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID); printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n", diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c index 97cf912f085..259f37e516f 100644 --- a/arch/sparc64/kernel/chmc.c +++ b/arch/sparc64/kernel/chmc.c @@ -17,6 +17,7 @@ #include <asm/spitfire.h> #include <asm/chmctrl.h> #include <asm/oplib.h> +#include <asm/prom.h> #include <asm/io.h> #define CHMCTRL_NDGRPS 2 @@ -67,7 +68,6 @@ struct bank_info { struct mctrl_info { struct list_head list; int portid; - int index; struct obp_mem_layout layout_prop; int layout_size; @@ -339,12 +339,13 @@ static void fetch_decode_regs(struct mctrl_info *mp) read_mcreg(mp, CHMCTRL_DECODE4)); } -static int init_one_mctrl(int node, int index) +static int init_one_mctrl(struct device_node *dp) { struct mctrl_info *mp = kmalloc(sizeof(*mp), GFP_KERNEL); - int portid = prom_getintdefault(node, "portid", -1); - struct linux_prom64_registers p_reg_prop; - int t; + int portid = of_getintprop_default(dp, "portid", -1); + struct linux_prom64_registers *regs; + void *pval; + int len; if (!mp) return -1; @@ -353,24 +354,21 @@ static int init_one_mctrl(int node, int index) goto fail; mp->portid = portid; - mp->layout_size = prom_getproplen(node, "memory-layout"); - if (mp->layout_size < 0) + pval = of_get_property(dp, "memory-layout", &len); + mp->layout_size = len; + if (!pval) mp->layout_size = 0; - if (mp->layout_size > sizeof(mp->layout_prop)) - goto fail; - - if (mp->layout_size > 0) - prom_getproperty(node, "memory-layout", - (char *) &mp->layout_prop, - mp->layout_size); + else { + if (mp->layout_size > sizeof(mp->layout_prop)) + goto fail; + memcpy(&mp->layout_prop, pval, len); + } - t = prom_getproperty(node, "reg", - (char *) &p_reg_prop, - sizeof(p_reg_prop)); - if (t < 0 || p_reg_prop.reg_size != 0x48) + regs = of_get_property(dp, "reg", NULL); + if (!regs || regs->reg_size != 0x48) goto fail; - mp->regs = ioremap(p_reg_prop.phys_addr, p_reg_prop.reg_size); + mp->regs = ioremap(regs->phys_addr, regs->reg_size); if (mp->regs == NULL) goto fail; @@ -384,13 +382,11 @@ static int init_one_mctrl(int node, int index) fetch_decode_regs(mp); - mp->index = index; - list_add(&mp->list, &mctrl_list); /* Report the device. */ - printk(KERN_INFO "chmc%d: US3 memory controller at %p [%s]\n", - mp->index, + printk(KERN_INFO "%s: US3 memory controller at %p [%s]\n", + dp->full_name, mp->regs, (mp->layout_size ? "ACTIVE" : "INACTIVE")); return 0; @@ -404,34 +400,19 @@ fail: return -1; } -static int __init probe_for_string(char *name, int index) -{ - int node = prom_getchild(prom_root_node); - - while ((node = prom_searchsiblings(node, name)) != 0) { - int ret = init_one_mctrl(node, index); - - if (!ret) - index++; - - node = prom_getsibling(node); - if (!node) - break; - } - - return index; -} - static int __init chmc_init(void) { - int index; + struct device_node *dp; /* This driver is only for cheetah platforms. */ if (tlb_type != cheetah && tlb_type != cheetah_plus) return -ENODEV; - index = probe_for_string("memory-controller", 0); - index = probe_for_string("mc-us3", index); + for_each_node_by_name(dp, "memory-controller") + init_one_mctrl(dp); + + for_each_node_by_name(dp, "mc-us3") + init_one_mctrl(dp); return 0; } diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index 11cc0caef59..7eb81d3954d 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -4,7 +4,6 @@ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/sched.h> diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index 007e8922cd1..f8ef2f2b9b3 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c @@ -4,7 +4,6 @@ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/threads.h> #include <linux/init.h> @@ -21,8 +20,6 @@ #include <asm/spitfire.h> #include <asm/timer.h> #include <asm/cpudata.h> -#include <asm/vdev.h> -#include <asm/irq.h> /* Used to synchronize acceses to NatSemi SUPER I/O chip configure * operations in asm/ns87303.h @@ -32,134 +29,6 @@ DEFINE_SPINLOCK(ns87303_lock); extern void cpu_probe(void); extern void central_probe(void); -u32 sun4v_vdev_devhandle; -int sun4v_vdev_root; - -struct vdev_intmap { - unsigned int phys; - unsigned int irq; - unsigned int cnode; - unsigned int cinterrupt; -}; - -struct vdev_intmask { - unsigned int phys; - unsigned int interrupt; - unsigned int __unused; -}; - -static struct vdev_intmap *vdev_intmap; -static int vdev_num_intmap; -static struct vdev_intmask vdev_intmask; - -static void __init sun4v_virtual_device_probe(void) -{ - struct linux_prom64_registers regs; - struct vdev_intmap *ip; - int node, sz, err; - - if (tlb_type != hypervisor) - return; - - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(node, "virtual-devices"); - if (!node) { - prom_printf("SUN4V: Fatal error, no virtual-devices node.\n"); - prom_halt(); - } - - sun4v_vdev_root = node; - - prom_getproperty(node, "reg", (char *)®s, sizeof(regs)); - sun4v_vdev_devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff; - - sz = prom_getproplen(node, "interrupt-map"); - if (sz <= 0) { - prom_printf("SUN4V: Error, no vdev interrupt-map.\n"); - prom_halt(); - } - - if ((sz % sizeof(*ip)) != 0) { - prom_printf("SUN4V: Bogus interrupt-map property size %d\n", - sz); - prom_halt(); - } - - vdev_intmap = ip = alloc_bootmem_low_pages(sz); - if (!vdev_intmap) { - prom_printf("SUN4V: Error, cannot allocate vdev_intmap.\n"); - prom_halt(); - } - - err = prom_getproperty(node, "interrupt-map", (char *) ip, sz); - if (err == -1) { - prom_printf("SUN4V: Fatal error, no vdev interrupt-map.\n"); - prom_halt(); - } - if (err != sz) { - prom_printf("SUN4V: Inconsistent interrupt-map size, " - "proplen(%d) vs getprop(%d).\n", sz,err); - prom_halt(); - } - - vdev_num_intmap = err / sizeof(*ip); - - err = prom_getproperty(node, "interrupt-map-mask", - (char *) &vdev_intmask, - sizeof(vdev_intmask)); - if (err <= 0) { - prom_printf("SUN4V: Fatal error, no vdev " - "interrupt-map-mask.\n"); - prom_halt(); - } - if (err % sizeof(vdev_intmask)) { - prom_printf("SUN4V: Bogus interrupt-map-mask " - "property size %d\n", err); - prom_halt(); - } - - printk("SUN4V: virtual-devices devhandle[%x]\n", - sun4v_vdev_devhandle); -} - -unsigned int sun4v_vdev_device_interrupt(unsigned int dev_node) -{ - unsigned int irq, reg; - int err, i; - - err = prom_getproperty(dev_node, "interrupts", - (char *) &irq, sizeof(irq)); - if (err <= 0) { - printk("VDEV: Cannot get \"interrupts\" " - "property for OBP node %x\n", dev_node); - return 0; - } - - err = prom_getproperty(dev_node, "reg", - (char *) ®, sizeof(reg)); - if (err <= 0) { - printk("VDEV: Cannot get \"reg\" " - "property for OBP node %x\n", dev_node); - return 0; - } - - for (i = 0; i < vdev_num_intmap; i++) { - if (vdev_intmap[i].phys == (reg & vdev_intmask.phys) && - vdev_intmap[i].irq == (irq & vdev_intmask.interrupt)) { - irq = vdev_intmap[i].cinterrupt; - break; - } - } - - if (i == vdev_num_intmap) { - printk("VDEV: No matching interrupt map entry " - "for OBP node %x\n", dev_node); - return 0; - } - - return sun4v_build_irq(sun4v_vdev_devhandle, irq, 5, 0); -} - static const char *cpu_mid_prop(void) { if (tlb_type == spitfire) @@ -167,38 +36,44 @@ static const char *cpu_mid_prop(void) return "portid"; } -static int get_cpu_mid(int prom_node) +static int get_cpu_mid(struct device_node *dp) { + struct property *prop; + if (tlb_type == hypervisor) { - struct linux_prom64_registers reg; + struct linux_prom64_registers *reg; + int len; - if (prom_getproplen(prom_node, "cpuid") == 4) - return prom_getintdefault(prom_node, "cpuid", 0); + prop = of_find_property(dp, "cpuid", &len); + if (prop && len == 4) + return *(int *) prop->value; - prom_getproperty(prom_node, "reg", (char *) ®, sizeof(reg)); - return (reg.phys_addr >> 32) & 0x0fffffffUL; + prop = of_find_property(dp, "reg", NULL); + reg = prop->value; + return (reg[0].phys_addr >> 32) & 0x0fffffffUL; } else { const char *prop_name = cpu_mid_prop(); - return prom_getintdefault(prom_node, prop_name, 0); + prop = of_find_property(dp, prop_name, NULL); + if (prop) + return *(int *) prop->value; + return 0; } } -static int check_cpu_node(int nd, int *cur_inst, - int (*compare)(int, int, void *), void *compare_arg, - int *prom_node, int *mid) +static int check_cpu_node(struct device_node *dp, int *cur_inst, + int (*compare)(struct device_node *, int, void *), + void *compare_arg, + struct device_node **dev_node, int *mid) { - char node_str[128]; - - prom_getstring(nd, "device_type", node_str, sizeof(node_str)); - if (strcmp(node_str, "cpu")) + if (strcmp(dp->type, "cpu")) return -ENODEV; - if (!compare(nd, *cur_inst, compare_arg)) { - if (prom_node) - *prom_node = nd; + if (!compare(dp, *cur_inst, compare_arg)) { + if (dev_node) + *dev_node = dp; if (mid) - *mid = get_cpu_mid(nd); + *mid = get_cpu_mid(dp); return 0; } @@ -207,25 +82,18 @@ static int check_cpu_node(int nd, int *cur_inst, return -ENODEV; } -static int __cpu_find_by(int (*compare)(int, int, void *), void *compare_arg, - int *prom_node, int *mid) +static int __cpu_find_by(int (*compare)(struct device_node *, int, void *), + void *compare_arg, + struct device_node **dev_node, int *mid) { - int nd, cur_inst, err; + struct device_node *dp; + int cur_inst; - nd = prom_root_node; cur_inst = 0; - - err = check_cpu_node(nd, &cur_inst, - compare, compare_arg, - prom_node, mid); - if (err == 0) - return 0; - - nd = prom_getchild(nd); - while ((nd = prom_getsibling(nd)) != 0) { - err = check_cpu_node(nd, &cur_inst, - compare, compare_arg, - prom_node, mid); + for_each_node_by_type(dp, "cpu") { + int err = check_cpu_node(dp, &cur_inst, + compare, compare_arg, + dev_node, mid); if (err == 0) return 0; } @@ -233,7 +101,7 @@ static int __cpu_find_by(int (*compare)(int, int, void *), void *compare_arg, return -ENODEV; } -static int cpu_instance_compare(int nd, int instance, void *_arg) +static int cpu_instance_compare(struct device_node *dp, int instance, void *_arg) { int desired_instance = (int) (long) _arg; @@ -242,27 +110,27 @@ static int cpu_instance_compare(int nd, int instance, void *_arg) return -ENODEV; } -int cpu_find_by_instance(int instance, int *prom_node, int *mid) +int cpu_find_by_instance(int instance, struct device_node **dev_node, int *mid) { return __cpu_find_by(cpu_instance_compare, (void *)(long)instance, - prom_node, mid); + dev_node, mid); } -static int cpu_mid_compare(int nd, int instance, void *_arg) +static int cpu_mid_compare(struct device_node *dp, int instance, void *_arg) { int desired_mid = (int) (long) _arg; int this_mid; - this_mid = get_cpu_mid(nd); + this_mid = get_cpu_mid(dp); if (this_mid == desired_mid) return 0; return -ENODEV; } -int cpu_find_by_mid(int mid, int *prom_node) +int cpu_find_by_mid(int mid, struct device_node **dev_node) { return __cpu_find_by(cpu_mid_compare, (void *)(long)mid, - prom_node, NULL); + dev_node, NULL); } void __init device_scan(void) @@ -274,50 +142,47 @@ void __init device_scan(void) #ifndef CONFIG_SMP { - int err, cpu_node, def; + struct device_node *dp; + int err, def; - err = cpu_find_by_instance(0, &cpu_node, NULL); + err = cpu_find_by_instance(0, &dp, NULL); if (err) { prom_printf("No cpu nodes, cannot continue\n"); prom_halt(); } - cpu_data(0).clock_tick = prom_getintdefault(cpu_node, - "clock-frequency", - 0); + cpu_data(0).clock_tick = + of_getintprop_default(dp, "clock-frequency", 0); def = ((tlb_type == hypervisor) ? (8 * 1024) : (16 * 1024)); - cpu_data(0).dcache_size = prom_getintdefault(cpu_node, - "dcache-size", - def); + cpu_data(0).dcache_size = of_getintprop_default(dp, + "dcache-size", + def); def = 32; cpu_data(0).dcache_line_size = - prom_getintdefault(cpu_node, "dcache-line-size", - def); + of_getintprop_default(dp, "dcache-line-size", def); def = 16 * 1024; - cpu_data(0).icache_size = prom_getintdefault(cpu_node, - "icache-size", - def); + cpu_data(0).icache_size = of_getintprop_default(dp, + "icache-size", + def); def = 32; cpu_data(0).icache_line_size = - prom_getintdefault(cpu_node, "icache-line-size", - def); + of_getintprop_default(dp, "icache-line-size", def); def = ((tlb_type == hypervisor) ? (3 * 1024 * 1024) : (4 * 1024 * 1024)); - cpu_data(0).ecache_size = prom_getintdefault(cpu_node, - "ecache-size", - def); + cpu_data(0).ecache_size = of_getintprop_default(dp, + "ecache-size", + def); def = 64; cpu_data(0).ecache_line_size = - prom_getintdefault(cpu_node, "ecache-line-size", - def); + of_getintprop_default(dp, "ecache-line-size", def); printk("CPU[0]: Caches " "D[sz(%d):line_sz(%d)] " "I[sz(%d):line_sz(%d)] " @@ -328,7 +193,6 @@ void __init device_scan(void) } #endif - sun4v_virtual_device_probe(); central_probe(); cpu_probe(); diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index c69504aa638..8a9b470e1b6 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -5,7 +5,6 @@ * Copyright (C) 1999 David S. Miller (davem@redhat.com) */ -#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> @@ -20,6 +19,8 @@ #include <asm/pbm.h> #include <asm/ebus.h> #include <asm/oplib.h> +#include <asm/prom.h> +#include <asm/of_device.h> #include <asm/bpp.h> #include <asm/irq.h> @@ -139,7 +140,7 @@ int ebus_dma_irq_enable(struct ebus_dma_info *p, int on) if (on) { if (p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) { - if (request_irq(p->irq, ebus_dma_irq, SA_SHIRQ, p->name, p)) + if (request_irq(p->irq, ebus_dma_irq, IRQF_SHARED, p->name, p)) return -EBUSY; } @@ -269,10 +270,6 @@ EXPORT_SYMBOL(ebus_dma_enable); struct linux_ebus *ebus_chain = NULL; -#ifdef CONFIG_SUN_AUXIO -extern void auxio_probe(void); -#endif - static inline void *ebus_alloc(size_t size) { void *mem; @@ -283,77 +280,22 @@ static inline void *ebus_alloc(size_t size) return mem; } -static void __init ebus_ranges_init(struct linux_ebus *ebus) -{ - int success; - - ebus->num_ebus_ranges = 0; - success = prom_getproperty(ebus->prom_node, "ranges", - (char *)ebus->ebus_ranges, - sizeof(ebus->ebus_ranges)); - if (success != -1) - ebus->num_ebus_ranges = (success/sizeof(struct linux_prom_ebus_ranges)); -} - -static void __init ebus_intmap_init(struct linux_ebus *ebus) -{ - int success; - - ebus->num_ebus_intmap = 0; - success = prom_getproperty(ebus->prom_node, "interrupt-map", - (char *)ebus->ebus_intmap, - sizeof(ebus->ebus_intmap)); - if (success == -1) - return; - - ebus->num_ebus_intmap = (success/sizeof(struct linux_prom_ebus_intmap)); - - success = prom_getproperty(ebus->prom_node, "interrupt-map-mask", - (char *)&ebus->ebus_intmask, - sizeof(ebus->ebus_intmask)); - if (success == -1) { - prom_printf("%s: can't get interrupt-map-mask\n", __FUNCTION__); - prom_halt(); - } -} - -int __init ebus_intmap_match(struct linux_ebus *ebus, - struct linux_prom_registers *reg, - int *interrupt) +static void __init fill_ebus_child(struct device_node *dp, + struct linux_ebus_child *dev, + int non_standard_regs) { - unsigned int hi, lo, irq; - int i; - - if (!ebus->num_ebus_intmap) - return 0; - - hi = reg->which_io & ebus->ebus_intmask.phys_hi; - lo = reg->phys_addr & ebus->ebus_intmask.phys_lo; - irq = *interrupt & ebus->ebus_intmask.interrupt; - for (i = 0; i < ebus->num_ebus_intmap; i++) { - if ((ebus->ebus_intmap[i].phys_hi == hi) && - (ebus->ebus_intmap[i].phys_lo == lo) && - (ebus->ebus_intmap[i].interrupt == irq)) { - *interrupt = ebus->ebus_intmap[i].cinterrupt; - return 0; - } - } - return -1; -} - -void __init fill_ebus_child(int node, struct linux_prom_registers *preg, - struct linux_ebus_child *dev, int non_standard_regs) -{ - int regs[PROMREG_MAX]; - int irqs[PROMREG_MAX]; + struct of_device *op; + int *regs; int i, len; - dev->prom_node = node; - prom_getstring(node, "name", dev->prom_name, sizeof(dev->prom_name)); - printk(" (%s)", dev->prom_name); + dev->prom_node = dp; + printk(" (%s)", dp->name); - len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs)); - dev->num_addrs = len / sizeof(regs[0]); + regs = of_get_property(dp, "reg", &len); + if (!regs) + dev->num_addrs = 0; + else + dev->num_addrs = len / sizeof(regs[0]); if (non_standard_regs) { /* This is to handle reg properties which are not @@ -370,30 +312,34 @@ void __init fill_ebus_child(int node, struct linux_prom_registers *preg, int rnum = regs[i]; if (rnum >= dev->parent->num_addrs) { prom_printf("UGH: property for %s was %d, need < %d\n", - dev->prom_name, len, dev->parent->num_addrs); - panic(__FUNCTION__); + dp->name, len, dev->parent->num_addrs); + prom_halt(); } dev->resource[i].start = dev->parent->resource[i].start; dev->resource[i].end = dev->parent->resource[i].end; dev->resource[i].flags = IORESOURCE_MEM; - dev->resource[i].name = dev->prom_name; + dev->resource[i].name = dp->name; } } - for (i = 0; i < PROMINTR_MAX; i++) - dev->irqs[i] = PCI_IRQ_NONE; - - len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs)); - if ((len == -1) || (len == 0)) { + op = of_find_device_by_node(dp); + if (!op) { dev->num_irqs = 0; + } else { + dev->num_irqs = op->num_irqs; + for (i = 0; i < dev->num_irqs; i++) + dev->irqs[i] = op->irqs[i]; + } + + if (!dev->num_irqs) { /* * Oh, well, some PROMs don't export interrupts * property to children of EBus devices... * * Be smart about PS/2 keyboard and mouse. */ - if (!strcmp(dev->parent->prom_name, "8042")) { - if (!strcmp(dev->prom_name, "kb_ps2")) { + if (!strcmp(dev->parent->prom_node->name, "8042")) { + if (!strcmp(dev->prom_node->name, "kb_ps2")) { dev->num_irqs = 1; dev->irqs[0] = dev->parent->irqs[0]; } else { @@ -401,103 +347,57 @@ void __init fill_ebus_child(int node, struct linux_prom_registers *preg, dev->irqs[0] = dev->parent->irqs[1]; } } - } else { - dev->num_irqs = len / sizeof(irqs[0]); - for (i = 0; i < dev->num_irqs; i++) { - struct pci_pbm_info *pbm = dev->bus->parent; - struct pci_controller_info *p = pbm->parent; - - if (ebus_intmap_match(dev->bus, preg, &irqs[i]) != -1) { - dev->irqs[i] = p->irq_build(pbm, - dev->bus->self, - irqs[i]); - } else { - /* If we get a bogus interrupt property, just - * record the raw value instead of punting. - */ - dev->irqs[i] = irqs[i]; - } - } } } static int __init child_regs_nonstandard(struct linux_ebus_device *dev) { - if (!strcmp(dev->prom_name, "i2c") || - !strcmp(dev->prom_name, "SUNW,lombus")) + if (!strcmp(dev->prom_node->name, "i2c") || + !strcmp(dev->prom_node->name, "SUNW,lombus")) return 1; return 0; } -void __init fill_ebus_device(int node, struct linux_ebus_device *dev) +static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev) { - struct linux_prom_registers regs[PROMREG_MAX]; struct linux_ebus_child *child; - int irqs[PROMINTR_MAX]; - int i, n, len; + struct of_device *op; + int i, len; - dev->prom_node = node; - prom_getstring(node, "name", dev->prom_name, sizeof(dev->prom_name)); - printk(" [%s", dev->prom_name); + dev->prom_node = dp; - len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs)); - if (len == -1) { + printk(" [%s", dp->name); + + op = of_find_device_by_node(dp); + if (!op) { dev->num_addrs = 0; - goto probe_interrupts; - } + dev->num_irqs = 0; + } else { + (void) of_get_property(dp, "reg", &len); + dev->num_addrs = len / sizeof(struct linux_prom_registers); - if (len % sizeof(struct linux_prom_registers)) { - prom_printf("UGH: proplen for %s was %d, need multiple of %d\n", - dev->prom_name, len, - (int)sizeof(struct linux_prom_registers)); - prom_halt(); - } - dev->num_addrs = len / sizeof(struct linux_prom_registers); - - for (i = 0; i < dev->num_addrs; i++) { - /* XXX Learn how to interpret ebus ranges... -DaveM */ - if (regs[i].which_io >= 0x10) - n = (regs[i].which_io - 0x10) >> 2; - else - n = regs[i].which_io; - - dev->resource[i].start = dev->bus->self->resource[n].start; - dev->resource[i].start += (unsigned long)regs[i].phys_addr; - dev->resource[i].end = - (dev->resource[i].start + (unsigned long)regs[i].reg_size - 1UL); - dev->resource[i].flags = IORESOURCE_MEM; - dev->resource[i].name = dev->prom_name; - request_resource(&dev->bus->self->resource[n], - &dev->resource[i]); + for (i = 0; i < dev->num_addrs; i++) + memcpy(&dev->resource[i], + &op->resource[i], + sizeof(struct resource)); + + dev->num_irqs = op->num_irqs; + for (i = 0; i < dev->num_irqs; i++) + dev->irqs[i] = op->irqs[i]; } -probe_interrupts: - for (i = 0; i < PROMINTR_MAX; i++) - dev->irqs[i] = PCI_IRQ_NONE; + dev->ofdev.node = dp; + dev->ofdev.dev.parent = &dev->bus->ofdev.dev; + dev->ofdev.dev.bus = &ebus_bus_type; + strcpy(dev->ofdev.dev.bus_id, dp->path_component_name); - len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs)); - if ((len == -1) || (len == 0)) { - dev->num_irqs = 0; - } else { - dev->num_irqs = len / sizeof(irqs[0]); - for (i = 0; i < dev->num_irqs; i++) { - struct pci_pbm_info *pbm = dev->bus->parent; - struct pci_controller_info *p = pbm->parent; - - if (ebus_intmap_match(dev->bus, ®s[0], &irqs[i]) != -1) { - dev->irqs[i] = p->irq_build(pbm, - dev->bus->self, - irqs[i]); - } else { - /* If we get a bogus interrupt property, just - * record the raw value instead of punting. - */ - dev->irqs[i] = irqs[i]; - } - } - } + /* Register with core */ + if (of_device_register(&dev->ofdev) != 0) + printk(KERN_DEBUG "ebus: device registration error for %s!\n", + dev->ofdev.dev.bus_id); - if ((node = prom_getchild(node))) { + dp = dp->child; + if (dp) { printk(" ->"); dev->children = ebus_alloc(sizeof(struct linux_ebus_child)); @@ -505,18 +405,18 @@ probe_interrupts: child->next = NULL; child->parent = dev; child->bus = dev->bus; - fill_ebus_child(node, ®s[0], - child, child_regs_nonstandard(dev)); + fill_ebus_child(dp, child, + child_regs_nonstandard(dev)); - while ((node = prom_getsibling(node)) != 0) { + while ((dp = dp->sibling) != NULL) { child->next = ebus_alloc(sizeof(struct linux_ebus_child)); child = child->next; child->next = NULL; child->parent = dev; child->bus = dev->bus; - fill_ebus_child(node, ®s[0], - child, child_regs_nonstandard(dev)); + fill_ebus_child(dp, child, + child_regs_nonstandard(dev)); } } printk("]"); @@ -543,7 +443,8 @@ void __init ebus_init(void) struct linux_ebus *ebus; struct pci_dev *pdev; struct pcidev_cookie *cookie; - int nd, ebusnd, is_rio; + struct device_node *dp; + int is_rio; int num_ebus = 0; pdev = find_next_ebus(NULL, &is_rio); @@ -553,20 +454,22 @@ void __init ebus_init(void) } cookie = pdev->sysdata; - ebusnd = cookie->prom_node; + dp = cookie->prom_node; ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus)); ebus->next = NULL; ebus->is_rio = is_rio; - while (ebusnd) { + while (dp) { + struct device_node *child; + /* SUNW,pci-qfe uses four empty ebuses on it. I think we should not consider them here, as they have half of the properties this code expects and once we do PCI hot-plug, we'd have to tweak with the ebus_chain in the runtime after initialization. -jj */ - if (!prom_getchild (ebusnd)) { + if (!dp->child) { pdev = find_next_ebus(pdev, &is_rio); if (!pdev) { if (ebus == ebus_chain) { @@ -578,22 +481,29 @@ void __init ebus_init(void) } ebus->is_rio = is_rio; cookie = pdev->sysdata; - ebusnd = cookie->prom_node; + dp = cookie->prom_node; continue; } printk("ebus%d:", num_ebus); - prom_getstring(ebusnd, "name", ebus->prom_name, sizeof(ebus->prom_name)); ebus->index = num_ebus; - ebus->prom_node = ebusnd; + ebus->prom_node = dp; ebus->self = pdev; ebus->parent = pbm = cookie->pbm; - ebus_ranges_init(ebus); - ebus_intmap_init(ebus); + ebus->ofdev.node = dp; + ebus->ofdev.dev.parent = &pdev->dev; + ebus->ofdev.dev.bus = &ebus_bus_type; + strcpy(ebus->ofdev.dev.bus_id, dp->path_component_name); + + /* Register with core */ + if (of_device_register(&ebus->ofdev) != 0) + printk(KERN_DEBUG "ebus: device registration error for %s!\n", + ebus->ofdev.dev.bus_id); - nd = prom_getchild(ebusnd); - if (!nd) + + child = dp->child; + if (!child) goto next_ebus; ebus->devices = ebus_alloc(sizeof(struct linux_ebus_device)); @@ -602,16 +512,16 @@ void __init ebus_init(void) dev->next = NULL; dev->children = NULL; dev->bus = ebus; - fill_ebus_device(nd, dev); + fill_ebus_device(child, dev); - while ((nd = prom_getsibling(nd)) != 0) { + while ((child = child->sibling) != NULL) { dev->next = ebus_alloc(sizeof(struct linux_ebus_device)); dev = dev->next; dev->next = NULL; dev->children = NULL; dev->bus = ebus; - fill_ebus_device(nd, dev); + fill_ebus_device(child, dev); } next_ebus: @@ -622,7 +532,7 @@ void __init ebus_init(void) break; cookie = pdev->sysdata; - ebusnd = cookie->prom_node; + dp = cookie->prom_node; ebus->next = ebus_alloc(sizeof(struct linux_ebus)); ebus = ebus->next; @@ -631,8 +541,4 @@ void __init ebus_init(void) ++num_ebus; } pci_dev_put(pdev); /* XXX for the case, when ebusnd is 0, is it OK? */ - -#ifdef CONFIG_SUN_AUXIO - auxio_probe(); -#endif } diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index 6d0b3ed77a0..0aaa35fc5a9 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -7,7 +7,6 @@ * Copyright (C) 1996,98,99 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include <linux/config.h> #include <linux/errno.h> #include <asm/head.h> @@ -22,6 +21,7 @@ #include <asm/estate.h> #include <asm/auxio.h> #include <asm/sfafsr.h> +#include <asm/pil.h> #define curptr g6 @@ -431,20 +431,16 @@ do_ivec: membar #Sync sethi %hi(ivector_table), %g2 - sllx %g3, 5, %g3 + sllx %g3, 3, %g3 or %g2, %lo(ivector_table), %g2 add %g2, %g3, %g3 - ldub [%g3 + 0x04], %g4 /* pil */ - mov 1, %g2 - sllx %g2, %g4, %g2 - sllx %g4, 2, %g4 TRAP_LOAD_IRQ_WORK(%g6, %g1) - lduw [%g6 + %g4], %g5 /* g5 = irq_work(cpu, pil) */ + lduw [%g6], %g5 /* g5 = irq_work(cpu) */ stw %g5, [%g3 + 0x00] /* bucket->irq_chain = g5 */ - stw %g3, [%g6 + %g4] /* irq_work(cpu, pil) = bucket */ - wr %g2, 0x0, %set_softint + stw %g3, [%g6] /* irq_work(cpu) = bucket */ + wr %g0, 1 << PIL_DEVICE_IRQ, %set_softint retry do_ivec_xcall: mov 0x50, %g1 diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S index 149383835c2..4b2bf9eb447 100644 --- a/arch/sparc64/kernel/etrap.S +++ b/arch/sparc64/kernel/etrap.S @@ -5,7 +5,6 @@ * Copyright (C) 1997, 1998, 1999 Jakub Jelinek (jj@ultra.linux.cz) */ -#include <linux/config.h> #include <asm/asi.h> #include <asm/pstate.h> diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index 31c5892f5ac..75684b56767 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -7,7 +7,6 @@ * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx) */ -#include <linux/config.h> #include <linux/version.h> #include <linux/errno.h> #include <linux/threads.h> diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 11e645c9ec5..4e64724cb9a 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -6,7 +6,6 @@ * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) */ -#include <linux/config.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/ptrace.h> @@ -22,6 +21,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/bootmem.h> +#include <linux/irq.h> #include <asm/ptrace.h> #include <asm/processor.h> @@ -33,6 +33,7 @@ #include <asm/iommu.h> #include <asm/upa.h> #include <asm/oplib.h> +#include <asm/prom.h> #include <asm/timer.h> #include <asm/smp.h> #include <asm/starfire.h> @@ -42,10 +43,6 @@ #include <asm/auxio.h> #include <asm/head.h> -#ifdef CONFIG_SMP -static void distribute_irqs(void); -#endif - /* UPA nodes send interrupt packet to UltraSparc with first data reg * value low 5 (7 on Starfire) bits holding the IRQ identifier being * delivered. We must translate this into a non-vector IRQ so we can @@ -57,10 +54,29 @@ static void distribute_irqs(void); * The IVEC handler does not need to act atomically, the PIL dispatch * code uses CAS to get an atomic snapshot of the list and clear it * at the same time. + * + * If you make changes to ino_bucket, please update hand coded assembler + * of the vectored interrupt trap handler(s) in entry.S and sun4v_ivec.S */ +struct ino_bucket { + /* Next handler in per-CPU IRQ worklist. We know that + * bucket pointers have the high 32-bits clear, so to + * save space we only store the bits we need. + */ +/*0x00*/unsigned int irq_chain; + /* Virtual interrupt number assigned to this INO. */ +/*0x04*/unsigned int virt_irq; +}; + +#define NUM_IVECS (IMAP_INR + 1) struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (SMP_CACHE_BYTES))); +#define __irq_ino(irq) \ + (((struct ino_bucket *)(unsigned long)(irq)) - &ivector_table[0]) +#define __bucket(irq) ((struct ino_bucket *)(unsigned long)(irq)) +#define __irq(bucket) ((unsigned int)(unsigned long)(bucket)) + /* This has to be in the main kernel image, it cannot be * turned into per-cpu data. The reason is that the main * kernel image is locked into the TLB and this structure @@ -68,71 +84,82 @@ struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (SMP_CACHE_BY * access to this structure takes a TLB miss it could cause * the 5-level sparc v9 trap stack to overflow. */ -struct irq_work_struct { - unsigned int irq_worklists[16]; -}; -struct irq_work_struct __irq_work[NR_CPUS]; -#define irq_work(__cpu, __pil) &(__irq_work[(__cpu)].irq_worklists[(__pil)]) +#define irq_work(__cpu) &(trap_block[(__cpu)].irq_worklist) -static struct irqaction *irq_action[NR_IRQS+1]; +static unsigned int virt_to_real_irq_table[NR_IRQS]; +static unsigned char virt_irq_cur = 1; -/* This only synchronizes entities which modify IRQ handler - * state and some selected user-level spots that want to - * read things in the table. IRQ handler processing orders - * its' accesses such that no locking is needed. - */ -static DEFINE_SPINLOCK(irq_action_lock); +static unsigned char virt_irq_alloc(unsigned int real_irq) +{ + unsigned char ent; + + BUILD_BUG_ON(NR_IRQS >= 256); + + ent = virt_irq_cur; + if (ent >= NR_IRQS) { + printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); + return 0; + } + + virt_irq_cur = ent + 1; + virt_to_real_irq_table[ent] = real_irq; -static void register_irq_proc (unsigned int irq); + return ent; +} + +#if 0 /* Currently unused. */ +static unsigned char real_to_virt_irq(unsigned int real_irq) +{ + struct ino_bucket *bucket = __bucket(real_irq); + + return bucket->virt_irq; +} +#endif + +static unsigned int virt_to_real_irq(unsigned char virt_irq) +{ + return virt_to_real_irq_table[virt_irq]; +} /* - * Upper 2b of irqaction->flags holds the ino. - * irqaction->mask holds the smp affinity information. + * /proc/interrupts printing: */ -#define put_ino_in_irqaction(action, irq) \ - action->flags &= 0xffffffffffffUL; \ - if (__bucket(irq) == &pil0_dummy_bucket) \ - action->flags |= 0xdeadUL << 48; \ - else \ - action->flags |= __irq_ino(irq) << 48; -#define get_ino_in_irqaction(action) (action->flags >> 48) - -#define put_smpaff_in_irqaction(action, smpaff) (action)->mask = (smpaff) -#define get_smpaff_in_irqaction(action) ((action)->mask) int show_interrupts(struct seq_file *p, void *v) { + int i = *(loff_t *) v, j; + struct irqaction * action; unsigned long flags; - int i = *(loff_t *) v; - struct irqaction *action; -#ifdef CONFIG_SMP - int j; -#endif - spin_lock_irqsave(&irq_action_lock, flags); - if (i <= NR_IRQS) { - if (!(action = *(i + irq_action))) - goto out_unlock; - seq_printf(p, "%3d: ", i); + if (i == 0) { + seq_printf(p, " "); + for_each_online_cpu(j) + seq_printf(p, "CPU%d ",j); + seq_putc(p, '\n'); + } + + if (i < NR_IRQS) { + spin_lock_irqsave(&irq_desc[i].lock, flags); + action = irq_desc[i].action; + if (!action) + goto skip; + seq_printf(p, "%3d: ",i); #ifndef CONFIG_SMP seq_printf(p, "%10u ", kstat_irqs(i)); #else - for_each_online_cpu(j) { - seq_printf(p, "%10u ", - kstat_cpu(j).irqs[i]); - } + for_each_online_cpu(j) + seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); #endif - seq_printf(p, " %s:%lx", action->name, - get_ino_in_irqaction(action)); - for (action = action->next; action; action = action->next) { - seq_printf(p, ", %s:%lx", action->name, - get_ino_in_irqaction(action)); - } + seq_printf(p, " %9s", irq_desc[i].chip->typename); + seq_printf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + seq_putc(p, '\n'); +skip: + spin_unlock_irqrestore(&irq_desc[i].lock, flags); } -out_unlock: - spin_unlock_irqrestore(&irq_action_lock, flags); - return 0; } @@ -173,694 +200,371 @@ static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid) return tid; } -/* Now these are always passed a true fully specified sun4u INO. */ -void enable_irq(unsigned int irq) -{ - struct ino_bucket *bucket = __bucket(irq); - unsigned long imap, cpuid; - - imap = bucket->imap; - if (imap == 0UL) - return; - - preempt_disable(); +struct irq_handler_data { + unsigned long iclr; + unsigned long imap; - /* This gets the physical processor ID, even on uniprocessor, - * so we can always program the interrupt target correctly. - */ - cpuid = real_hard_smp_processor_id(); - - if (tlb_type == hypervisor) { - unsigned int ino = __irq_ino(irq); - int err; - - err = sun4v_intr_settarget(ino, cpuid); - if (err != HV_EOK) - printk("sun4v_intr_settarget(%x,%lu): err(%d)\n", - ino, cpuid, err); - err = sun4v_intr_setenabled(ino, HV_INTR_ENABLED); - if (err != HV_EOK) - printk("sun4v_intr_setenabled(%x): err(%d)\n", - ino, err); - } else { - unsigned int tid = sun4u_compute_tid(imap, cpuid); - - /* NOTE NOTE NOTE, IGN and INO are read-only, IGN is a product - * of this SYSIO's preconfigured IGN in the SYSIO Control - * Register, the hardware just mirrors that value here. - * However for Graphics and UPA Slave devices the full - * IMAP_INR field can be set by the programmer here. - * - * Things like FFB can now be handled via the new IRQ - * mechanism. - */ - upa_writel(tid | IMAP_VALID, imap); - } - - preempt_enable(); -} + void (*pre_handler)(unsigned int, void *, void *); + void *pre_handler_arg1; + void *pre_handler_arg2; +}; -/* This now gets passed true ino's as well. */ -void disable_irq(unsigned int irq) +static inline struct ino_bucket *virt_irq_to_bucket(unsigned int virt_irq) { - struct ino_bucket *bucket = __bucket(irq); - unsigned long imap; - - imap = bucket->imap; - if (imap != 0UL) { - if (tlb_type == hypervisor) { - unsigned int ino = __irq_ino(irq); - int err; - - err = sun4v_intr_setenabled(ino, HV_INTR_DISABLED); - if (err != HV_EOK) - printk("sun4v_intr_setenabled(%x): " - "err(%d)\n", ino, err); - } else { - u32 tmp; - - /* NOTE: We do not want to futz with the IRQ clear registers - * and move the state to IDLE, the SCSI code does call - * disable_irq() to assure atomicity in the queue cmd - * SCSI adapter driver code. Thus we'd lose interrupts. - */ - tmp = upa_readl(imap); - tmp &= ~IMAP_VALID; - upa_writel(tmp, imap); - } - } -} + unsigned int real_irq = virt_to_real_irq(virt_irq); + struct ino_bucket *bucket = NULL; -/* The timer is the one "weird" interrupt which is generated by - * the CPU %tick register and not by some normal vectored interrupt - * source. To handle this special case, we use this dummy INO bucket. - */ -static struct irq_desc pil0_dummy_desc; -static struct ino_bucket pil0_dummy_bucket = { - .irq_info = &pil0_dummy_desc, -}; + if (likely(real_irq)) + bucket = __bucket(real_irq); -static void build_irq_error(const char *msg, unsigned int ino, int pil, int inofixup, - unsigned long iclr, unsigned long imap, - struct ino_bucket *bucket) -{ - prom_printf("IRQ: INO %04x (%d:%016lx:%016lx) --> " - "(%d:%d:%016lx:%016lx), halting...\n", - ino, bucket->pil, bucket->iclr, bucket->imap, - pil, inofixup, iclr, imap); - prom_halt(); + return bucket; } -unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long imap) +#ifdef CONFIG_SMP +static int irq_choose_cpu(unsigned int virt_irq) { - struct ino_bucket *bucket; - int ino; + cpumask_t mask = irq_desc[virt_irq].affinity; + int cpuid; - if (pil == 0) { - if (iclr != 0UL || imap != 0UL) { - prom_printf("Invalid dummy bucket for PIL0 (%lx:%lx)\n", - iclr, imap); - prom_halt(); - } - return __irq(&pil0_dummy_bucket); - } + if (cpus_equal(mask, CPU_MASK_ALL)) { + static int irq_rover; + static DEFINE_SPINLOCK(irq_rover_lock); + unsigned long flags; - BUG_ON(tlb_type == hypervisor); + /* Round-robin distribution... */ + do_round_robin: + spin_lock_irqsave(&irq_rover_lock, flags); - /* RULE: Both must be specified in all other cases. */ - if (iclr == 0UL || imap == 0UL) { - prom_printf("Invalid build_irq %d %d %016lx %016lx\n", - pil, inofixup, iclr, imap); - prom_halt(); - } - - ino = (upa_readl(imap) & (IMAP_IGN | IMAP_INO)) + inofixup; - if (ino > NUM_IVECS) { - prom_printf("Invalid INO %04x (%d:%d:%016lx:%016lx)\n", - ino, pil, inofixup, iclr, imap); - prom_halt(); - } + while (!cpu_online(irq_rover)) { + if (++irq_rover >= NR_CPUS) + irq_rover = 0; + } + cpuid = irq_rover; + do { + if (++irq_rover >= NR_CPUS) + irq_rover = 0; + } while (!cpu_online(irq_rover)); - bucket = &ivector_table[ino]; - if (bucket->flags & IBF_ACTIVE) - build_irq_error("IRQ: Trying to build active INO bucket.\n", - ino, pil, inofixup, iclr, imap, bucket); + spin_unlock_irqrestore(&irq_rover_lock, flags); + } else { + cpumask_t tmp; - if (bucket->irq_info) { - if (bucket->imap != imap || bucket->iclr != iclr) - build_irq_error("IRQ: Trying to reinit INO bucket.\n", - ino, pil, inofixup, iclr, imap, bucket); + cpus_and(tmp, cpu_online_map, mask); - goto out; - } + if (cpus_empty(tmp)) + goto do_round_robin; - bucket->irq_info = kzalloc(sizeof(struct irq_desc), GFP_ATOMIC); - if (!bucket->irq_info) { - prom_printf("IRQ: Error, kmalloc(irq_desc) failed.\n"); - prom_halt(); + cpuid = first_cpu(tmp); } - /* Ok, looks good, set it up. Don't touch the irq_chain or - * the pending flag. - */ - bucket->imap = imap; - bucket->iclr = iclr; - bucket->pil = pil; - bucket->flags = 0; - -out: - return __irq(bucket); + return cpuid; } - -unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino, int pil, unsigned char flags) +#else +static int irq_choose_cpu(unsigned int virt_irq) { - struct ino_bucket *bucket; - unsigned long sysino; + return real_hard_smp_processor_id(); +} +#endif - sysino = sun4v_devino_to_sysino(devhandle, devino); +static void sun4u_irq_enable(unsigned int virt_irq) +{ + irq_desc_t *desc = irq_desc + virt_irq; + struct irq_handler_data *data = desc->handler_data; - bucket = &ivector_table[sysino]; + if (likely(data)) { + unsigned long cpuid, imap; + unsigned int tid; - /* Catch accidental accesses to these things. IMAP/ICLR handling - * is done by hypervisor calls on sun4v platforms, not by direct - * register accesses. - * - * But we need to make them look unique for the disable_irq() logic - * in free_irq(). - */ - bucket->imap = ~0UL - sysino; - bucket->iclr = ~0UL - sysino; + cpuid = irq_choose_cpu(virt_irq); + imap = data->imap; - bucket->pil = pil; - bucket->flags = flags; + tid = sun4u_compute_tid(imap, cpuid); - bucket->irq_info = kzalloc(sizeof(struct irq_desc), GFP_ATOMIC); - if (!bucket->irq_info) { - prom_printf("IRQ: Error, kmalloc(irq_desc) failed.\n"); - prom_halt(); + upa_writel(tid | IMAP_VALID, imap); } - - return __irq(bucket); } -static void atomic_bucket_insert(struct ino_bucket *bucket) +static void sun4u_irq_disable(unsigned int virt_irq) { - unsigned long pstate; - unsigned int *ent; + irq_desc_t *desc = irq_desc + virt_irq; + struct irq_handler_data *data = desc->handler_data; - __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); - __asm__ __volatile__("wrpr %0, %1, %%pstate" - : : "r" (pstate), "i" (PSTATE_IE)); - ent = irq_work(smp_processor_id(), bucket->pil); - bucket->irq_chain = *ent; - *ent = __irq(bucket); - __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); -} + if (likely(data)) { + unsigned long imap = data->imap; + u32 tmp = upa_readl(imap); -static int check_irq_sharing(int pil, unsigned long irqflags) -{ - struct irqaction *action, *tmp; - - action = *(irq_action + pil); - if (action) { - if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) { - for (tmp = action; tmp->next; tmp = tmp->next) - ; - } else { - return -EBUSY; - } + tmp &= ~IMAP_VALID; + upa_writel(tmp, imap); } - return 0; } -static void append_irq_action(int pil, struct irqaction *action) +static void sun4u_irq_end(unsigned int virt_irq) { - struct irqaction **pp = irq_action + pil; + irq_desc_t *desc = irq_desc + virt_irq; + struct irq_handler_data *data = desc->handler_data; - while (*pp) - pp = &((*pp)->next); - *pp = action; + if (likely(data)) + upa_writel(ICLR_IDLE, data->iclr); } -static struct irqaction *get_action_slot(struct ino_bucket *bucket) +static void sun4v_irq_enable(unsigned int virt_irq) { - struct irq_desc *desc = bucket->irq_info; - int max_irq, i; + struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); + unsigned int ino = bucket - &ivector_table[0]; - max_irq = 1; - if (bucket->flags & IBF_PCI) - max_irq = MAX_IRQ_DESC_ACTION; - for (i = 0; i < max_irq; i++) { - struct irqaction *p = &desc->action[i]; - u32 mask = (1 << i); + if (likely(bucket)) { + unsigned long cpuid; + int err; - if (desc->action_active_mask & mask) - continue; + cpuid = irq_choose_cpu(virt_irq); - desc->action_active_mask |= mask; - return p; + err = sun4v_intr_settarget(ino, cpuid); + if (err != HV_EOK) + printk("sun4v_intr_settarget(%x,%lu): err(%d)\n", + ino, cpuid, err); + err = sun4v_intr_setenabled(ino, HV_INTR_ENABLED); + if (err != HV_EOK) + printk("sun4v_intr_setenabled(%x): err(%d)\n", + ino, err); } - return NULL; } -int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char *name, void *dev_id) +static void sun4v_irq_disable(unsigned int virt_irq) { - struct irqaction *action; - struct ino_bucket *bucket = __bucket(irq); - unsigned long flags; - int pending = 0; - - if (unlikely(!handler)) - return -EINVAL; - - if (unlikely(!bucket->irq_info)) - return -ENODEV; - - if ((bucket != &pil0_dummy_bucket) && (irqflags & SA_SAMPLE_RANDOM)) { - /* - * This function might sleep, we want to call it first, - * outside of the atomic block. In SA_STATIC_ALLOC case, - * random driver's kmalloc will fail, but it is safe. - * If already initialized, random driver will not reinit. - * Yes, this might clear the entropy pool if the wrong - * driver is attempted to be loaded, without actually - * installing a new handler, but is this really a problem, - * only the sysadmin is able to do this. - */ - rand_initialize_irq(irq); - } - - spin_lock_irqsave(&irq_action_lock, flags); + struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); + unsigned int ino = bucket - &ivector_table[0]; - if (check_irq_sharing(bucket->pil, irqflags)) { - spin_unlock_irqrestore(&irq_action_lock, flags); - return -EBUSY; - } - - action = get_action_slot(bucket); - if (!action) { - spin_unlock_irqrestore(&irq_action_lock, flags); - return -ENOMEM; - } - - bucket->flags |= IBF_ACTIVE; - pending = 0; - if (bucket != &pil0_dummy_bucket) { - pending = bucket->pending; - if (pending) - bucket->pending = 0; - } - - action->handler = handler; - action->flags = irqflags; - action->name = name; - action->next = NULL; - action->dev_id = dev_id; - put_ino_in_irqaction(action, irq); - put_smpaff_in_irqaction(action, CPU_MASK_NONE); - - append_irq_action(bucket->pil, action); - - enable_irq(irq); + if (likely(bucket)) { + int err; - /* We ate the IVEC already, this makes sure it does not get lost. */ - if (pending) { - atomic_bucket_insert(bucket); - set_softint(1 << bucket->pil); + err = sun4v_intr_setenabled(ino, HV_INTR_DISABLED); + if (err != HV_EOK) + printk("sun4v_intr_setenabled(%x): " + "err(%d)\n", ino, err); } - - spin_unlock_irqrestore(&irq_action_lock, flags); - - if (bucket != &pil0_dummy_bucket) - register_irq_proc(__irq_ino(irq)); - -#ifdef CONFIG_SMP - distribute_irqs(); -#endif - return 0; } -EXPORT_SYMBOL(request_irq); - -static struct irqaction *unlink_irq_action(unsigned int irq, void *dev_id) +static void sun4v_irq_end(unsigned int virt_irq) { - struct ino_bucket *bucket = __bucket(irq); - struct irqaction *action, **pp; - - pp = irq_action + bucket->pil; - action = *pp; - if (unlikely(!action)) - return NULL; + struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); + unsigned int ino = bucket - &ivector_table[0]; - if (unlikely(!action->handler)) { - printk("Freeing free IRQ %d\n", bucket->pil); - return NULL; - } + if (likely(bucket)) { + int err; - while (action && action->dev_id != dev_id) { - pp = &action->next; - action = *pp; + err = sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE); + if (err != HV_EOK) + printk("sun4v_intr_setstate(%x): " + "err(%d)\n", ino, err); } - - if (likely(action)) - *pp = action->next; - - return action; } -void free_irq(unsigned int irq, void *dev_id) +static void run_pre_handler(unsigned int virt_irq) { - struct irqaction *action; - struct ino_bucket *bucket; - unsigned long flags; - - spin_lock_irqsave(&irq_action_lock, flags); - - action = unlink_irq_action(irq, dev_id); - - spin_unlock_irqrestore(&irq_action_lock, flags); - - if (unlikely(!action)) - return; - - synchronize_irq(irq); + struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); + irq_desc_t *desc = irq_desc + virt_irq; + struct irq_handler_data *data = desc->handler_data; - spin_lock_irqsave(&irq_action_lock, flags); + if (likely(data->pre_handler)) { + data->pre_handler(__irq_ino(__irq(bucket)), + data->pre_handler_arg1, + data->pre_handler_arg2); + } +} - bucket = __bucket(irq); - if (bucket != &pil0_dummy_bucket) { - struct irq_desc *desc = bucket->irq_info; - int ent, i; +static struct hw_interrupt_type sun4u_irq = { + .typename = "sun4u", + .enable = sun4u_irq_enable, + .disable = sun4u_irq_disable, + .end = sun4u_irq_end, +}; - for (i = 0; i < MAX_IRQ_DESC_ACTION; i++) { - struct irqaction *p = &desc->action[i]; +static struct hw_interrupt_type sun4u_irq_ack = { + .typename = "sun4u+ack", + .enable = sun4u_irq_enable, + .disable = sun4u_irq_disable, + .ack = run_pre_handler, + .end = sun4u_irq_end, +}; - if (p == action) { - desc->action_active_mask &= ~(1 << i); - break; - } - } +static struct hw_interrupt_type sun4v_irq = { + .typename = "sun4v", + .enable = sun4v_irq_enable, + .disable = sun4v_irq_disable, + .end = sun4v_irq_end, +}; - if (!desc->action_active_mask) { - unsigned long imap = bucket->imap; - - /* This unique interrupt source is now inactive. */ - bucket->flags &= ~IBF_ACTIVE; - - /* See if any other buckets share this bucket's IMAP - * and are still active. - */ - for (ent = 0; ent < NUM_IVECS; ent++) { - struct ino_bucket *bp = &ivector_table[ent]; - if (bp != bucket && - bp->imap == imap && - (bp->flags & IBF_ACTIVE) != 0) - break; - } +static struct hw_interrupt_type sun4v_irq_ack = { + .typename = "sun4v+ack", + .enable = sun4v_irq_enable, + .disable = sun4v_irq_disable, + .ack = run_pre_handler, + .end = sun4v_irq_end, +}; - /* Only disable when no other sub-irq levels of - * the same IMAP are active. - */ - if (ent == NUM_IVECS) - disable_irq(irq); - } - } +void irq_install_pre_handler(int virt_irq, + void (*func)(unsigned int, void *, void *), + void *arg1, void *arg2) +{ + irq_desc_t *desc = irq_desc + virt_irq; + struct irq_handler_data *data = desc->handler_data; - spin_unlock_irqrestore(&irq_action_lock, flags); -} + data->pre_handler = func; + data->pre_handler_arg1 = arg1; + data->pre_handler_arg2 = arg2; -EXPORT_SYMBOL(free_irq); + if (desc->chip == &sun4u_irq_ack || + desc->chip == &sun4v_irq_ack) + return; -#ifdef CONFIG_SMP -void synchronize_irq(unsigned int irq) -{ - struct ino_bucket *bucket = __bucket(irq); - -#if 0 - /* The following is how I wish I could implement this. - * Unfortunately the ICLR registers are read-only, you can - * only write ICLR_foo values to them. To get the current - * IRQ status you would need to get at the IRQ diag registers - * in the PCI/SBUS controller and the layout of those vary - * from one controller to the next, sigh... -DaveM - */ - unsigned long iclr = bucket->iclr; - - while (1) { - u32 tmp = upa_readl(iclr); - - if (tmp == ICLR_TRANSMIT || - tmp == ICLR_PENDING) { - cpu_relax(); - continue; - } - break; - } -#else - /* So we have to do this with a INPROGRESS bit just like x86. */ - while (bucket->flags & IBF_INPROGRESS) - cpu_relax(); -#endif + desc->chip = (desc->chip == &sun4u_irq ? + &sun4u_irq_ack : &sun4v_irq_ack); } -#endif /* CONFIG_SMP */ -static void process_bucket(int irq, struct ino_bucket *bp, struct pt_regs *regs) +unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) { - struct irq_desc *desc = bp->irq_info; - unsigned char flags = bp->flags; - u32 action_mask, i; - int random; + struct ino_bucket *bucket; + struct irq_handler_data *data; + irq_desc_t *desc; + int ino; - bp->flags |= IBF_INPROGRESS; + BUG_ON(tlb_type == hypervisor); - if (unlikely(!(flags & IBF_ACTIVE))) { - bp->pending = 1; - goto out; + ino = (upa_readl(imap) & (IMAP_IGN | IMAP_INO)) + inofixup; + bucket = &ivector_table[ino]; + if (!bucket->virt_irq) { + bucket->virt_irq = virt_irq_alloc(__irq(bucket)); + irq_desc[bucket->virt_irq].chip = &sun4u_irq; } - if (desc->pre_handler) - desc->pre_handler(bp, - desc->pre_handler_arg1, - desc->pre_handler_arg2); - - action_mask = desc->action_active_mask; - random = 0; - for (i = 0; i < MAX_IRQ_DESC_ACTION; i++) { - struct irqaction *p = &desc->action[i]; - u32 mask = (1 << i); - - if (!(action_mask & mask)) - continue; - - action_mask &= ~mask; - - if (p->handler(__irq(bp), p->dev_id, regs) == IRQ_HANDLED) - random |= p->flags; + desc = irq_desc + bucket->virt_irq; + if (unlikely(desc->handler_data)) + goto out; - if (!action_mask) - break; + data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); + if (unlikely(!data)) { + prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); + prom_halt(); } - if (bp->pil != 0) { - if (tlb_type == hypervisor) { - unsigned int ino = __irq_ino(bp); - int err; - - err = sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE); - if (err != HV_EOK) - printk("sun4v_intr_setstate(%x): " - "err(%d)\n", ino, err); - } else { - upa_writel(ICLR_IDLE, bp->iclr); - } + desc->handler_data = data; + + data->imap = imap; + data->iclr = iclr; - /* Test and add entropy */ - if (random & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq); - } out: - bp->flags &= ~IBF_INPROGRESS; + return bucket->virt_irq; } -void handler_irq(int irq, struct pt_regs *regs) +unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino) { - struct ino_bucket *bp; - int cpu = smp_processor_id(); + struct ino_bucket *bucket; + struct irq_handler_data *data; + unsigned long sysino; + irq_desc_t *desc; -#ifndef CONFIG_SMP - /* - * Check for TICK_INT on level 14 softint. - */ - { - unsigned long clr_mask = 1 << irq; - unsigned long tick_mask = tick_ops->softint_mask; + BUG_ON(tlb_type != hypervisor); - if ((irq == 14) && (get_softint() & tick_mask)) { - irq = 0; - clr_mask = tick_mask; - } - clear_softint(clr_mask); + sysino = sun4v_devino_to_sysino(devhandle, devino); + bucket = &ivector_table[sysino]; + if (!bucket->virt_irq) { + bucket->virt_irq = virt_irq_alloc(__irq(bucket)); + irq_desc[bucket->virt_irq].chip = &sun4v_irq; } -#else - clear_softint(1 << irq); -#endif - irq_enter(); - kstat_this_cpu.irqs[irq]++; - - /* Sliiiick... */ -#ifndef CONFIG_SMP - bp = ((irq != 0) ? - __bucket(xchg32(irq_work(cpu, irq), 0)) : - &pil0_dummy_bucket); -#else - bp = __bucket(xchg32(irq_work(cpu, irq), 0)); -#endif - while (bp) { - struct ino_bucket *nbp = __bucket(bp->irq_chain); + desc = irq_desc + bucket->virt_irq; + if (unlikely(desc->handler_data)) + goto out; - bp->irq_chain = 0; - process_bucket(irq, bp, regs); - bp = nbp; + data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); + if (unlikely(!data)) { + prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); + prom_halt(); } - irq_exit(); -} + desc->handler_data = data; -#ifdef CONFIG_BLK_DEV_FD -extern irqreturn_t floppy_interrupt(int, void *, struct pt_regs *); + /* Catch accidental accesses to these things. IMAP/ICLR handling + * is done by hypervisor calls on sun4v platforms, not by direct + * register accesses. + */ + data->imap = ~0UL; + data->iclr = ~0UL; -/* XXX No easy way to include asm/floppy.h XXX */ -extern unsigned char *pdma_vaddr; -extern unsigned long pdma_size; -extern volatile int doing_pdma; -extern unsigned long fdc_status; +out: + return bucket->virt_irq; +} -irqreturn_t sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) +void hw_resend_irq(struct hw_interrupt_type *handler, unsigned int virt_irq) { - if (likely(doing_pdma)) { - void __iomem *stat = (void __iomem *) fdc_status; - unsigned char *vaddr = pdma_vaddr; - unsigned long size = pdma_size; - u8 val; - - while (size) { - val = readb(stat); - if (unlikely(!(val & 0x80))) { - pdma_vaddr = vaddr; - pdma_size = size; - return IRQ_HANDLED; - } - if (unlikely(!(val & 0x20))) { - pdma_vaddr = vaddr; - pdma_size = size; - doing_pdma = 0; - goto main_interrupt; - } - if (val & 0x40) { - /* read */ - *vaddr++ = readb(stat + 1); - } else { - unsigned char data = *vaddr++; - - /* write */ - writeb(data, stat + 1); - } - size--; - } - - pdma_vaddr = vaddr; - pdma_size = size; - - /* Send Terminal Count pulse to floppy controller. */ - val = readb(auxio_register); - val |= AUXIO_AUX1_FTCNT; - writeb(val, auxio_register); - val &= ~AUXIO_AUX1_FTCNT; - writeb(val, auxio_register); - - doing_pdma = 0; - } + struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); + unsigned long pstate; + unsigned int *ent; -main_interrupt: - return floppy_interrupt(irq, dev_cookie, regs); + __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); + __asm__ __volatile__("wrpr %0, %1, %%pstate" + : : "r" (pstate), "i" (PSTATE_IE)); + ent = irq_work(smp_processor_id()); + bucket->irq_chain = *ent; + *ent = __irq(bucket); + set_softint(1 << PIL_DEVICE_IRQ); + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); } -EXPORT_SYMBOL(sparc_floppy_irq); -#endif -/* We really don't need these at all on the Sparc. We only have - * stubs here because they are exported to modules. - */ -unsigned long probe_irq_on(void) +void ack_bad_irq(unsigned int virt_irq) { - return 0; -} + struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); + unsigned int ino = 0xdeadbeef; -EXPORT_SYMBOL(probe_irq_on); + if (bucket) + ino = bucket - &ivector_table[0]; -int probe_irq_off(unsigned long mask) -{ - return 0; + printk(KERN_CRIT "Unexpected IRQ from ino[%x] virt_irq[%u]\n", + ino, virt_irq); } -EXPORT_SYMBOL(probe_irq_off); +#ifndef CONFIG_SMP +extern irqreturn_t timer_interrupt(int, void *, struct pt_regs *); -#ifdef CONFIG_SMP -static int retarget_one_irq(struct irqaction *p, int goal_cpu) +void timer_irq(int irq, struct pt_regs *regs) { - struct ino_bucket *bucket = get_ino_in_irqaction(p) + ivector_table; + unsigned long clr_mask = 1 << irq; + unsigned long tick_mask = tick_ops->softint_mask; - while (!cpu_online(goal_cpu)) { - if (++goal_cpu >= NR_CPUS) - goal_cpu = 0; + if (get_softint() & tick_mask) { + irq = 0; + clr_mask = tick_mask; } + clear_softint(clr_mask); - if (tlb_type == hypervisor) { - unsigned int ino = __irq_ino(bucket); - - sun4v_intr_settarget(ino, goal_cpu); - sun4v_intr_setenabled(ino, HV_INTR_ENABLED); - } else { - unsigned long imap = bucket->imap; - unsigned int tid = sun4u_compute_tid(imap, goal_cpu); - - upa_writel(tid | IMAP_VALID, imap); - } + irq_enter(); - do { - if (++goal_cpu >= NR_CPUS) - goal_cpu = 0; - } while (!cpu_online(goal_cpu)); + kstat_this_cpu.irqs[0]++; + timer_interrupt(irq, NULL, regs); - return goal_cpu; + irq_exit(); } +#endif -/* Called from request_irq. */ -static void distribute_irqs(void) +void handler_irq(int irq, struct pt_regs *regs) { - unsigned long flags; - int cpu, level; + struct ino_bucket *bucket; - spin_lock_irqsave(&irq_action_lock, flags); - cpu = 0; + clear_softint(1 << irq); - /* - * Skip the timer at [0], and very rare error/power intrs at [15]. - * Also level [12], it causes problems on Ex000 systems. - */ - for (level = 1; level < NR_IRQS; level++) { - struct irqaction *p = irq_action[level]; + irq_enter(); - if (level == 12) - continue; + /* Sliiiick... */ + bucket = __bucket(xchg32(irq_work(smp_processor_id()), 0)); + while (bucket) { + struct ino_bucket *next = __bucket(bucket->irq_chain); - while(p) { - cpu = retarget_one_irq(p, cpu); - p = p->next; - } + bucket->irq_chain = 0; + __do_IRQ(bucket->virt_irq, regs); + + bucket = next; } - spin_unlock_irqrestore(&irq_action_lock, flags); + + irq_exit(); } -#endif struct sun5_timer { u64 count0; @@ -874,23 +578,29 @@ static u64 prom_limit0, prom_limit1; static void map_prom_timers(void) { - unsigned int addr[3]; - int tnode, err; + struct device_node *dp; + unsigned int *addr; /* PROM timer node hangs out in the top level of device siblings... */ - tnode = prom_finddevice("/counter-timer"); + dp = of_find_node_by_path("/"); + dp = dp->child; + while (dp) { + if (!strcmp(dp->name, "counter-timer")) + break; + dp = dp->sibling; + } /* Assume if node is not present, PROM uses different tick mechanism * which we should not care about. */ - if (tnode == 0 || tnode == -1) { + if (!dp) { prom_timers = (struct sun5_timer *) 0; return; } /* If PROM is really using this, it must be mapped by him. */ - err = prom_getproperty(tnode, "address", (char *)addr, sizeof(addr)); - if (err == -1) { + addr = of_get_property(dp, "address", NULL); + if (!addr) { prom_printf("PROM does not have timer mapped, trying to continue.\n"); prom_timers = (struct sun5_timer *) 0; return; @@ -929,7 +639,7 @@ void init_irqwork_curcpu(void) { int cpu = hard_smp_processor_id(); - memset(__irq_work + cpu, 0, sizeof(struct irq_work_struct)); + trap_block[cpu].irq_worklist = 0; } static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type) @@ -1037,6 +747,10 @@ void __cpuinit sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int } } +static struct irqaction timer_irq_action = { + .name = "timer", +}; + /* Only invoked on boot processor. */ void __init init_IRQ(void) { @@ -1064,109 +778,6 @@ void __init init_IRQ(void) : /* No outputs */ : "i" (PSTATE_IE) : "g1"); -} - -static struct proc_dir_entry * root_irq_dir; -static struct proc_dir_entry * irq_dir [NUM_IVECS]; - -#ifdef CONFIG_SMP - -static int irq_affinity_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - struct ino_bucket *bp = ivector_table + (long)data; - struct irq_desc *desc = bp->irq_info; - struct irqaction *ap = desc->action; - cpumask_t mask; - int len; - - mask = get_smpaff_in_irqaction(ap); - if (cpus_empty(mask)) - mask = cpu_online_map; - - len = cpumask_scnprintf(page, count, mask); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -static inline void set_intr_affinity(int irq, cpumask_t hw_aff) -{ - struct ino_bucket *bp = ivector_table + irq; - struct irq_desc *desc = bp->irq_info; - struct irqaction *ap = desc->action; - - /* Users specify affinity in terms of hw cpu ids. - * As soon as we do this, handler_irq() might see and take action. - */ - put_smpaff_in_irqaction(ap, hw_aff); - - /* Migration is simply done by the next cpu to service this - * interrupt. - */ -} - -static int irq_affinity_write_proc (struct file *file, const char __user *buffer, - unsigned long count, void *data) -{ - int irq = (long) data, full_count = count, err; - cpumask_t new_value; - - err = cpumask_parse(buffer, count, new_value); - - /* - * Do not allow disabling IRQs completely - it's a too easy - * way to make the system unusable accidentally :-) At least - * one online CPU still has to be targeted. - */ - cpus_and(new_value, new_value, cpu_online_map); - if (cpus_empty(new_value)) - return -EINVAL; - - set_intr_affinity(irq, new_value); - return full_count; + irq_desc[0].action = &timer_irq_action; } - -#endif - -#define MAX_NAMELEN 10 - -static void register_irq_proc (unsigned int irq) -{ - char name [MAX_NAMELEN]; - - if (!root_irq_dir || irq_dir[irq]) - return; - - memset(name, 0, MAX_NAMELEN); - sprintf(name, "%x", irq); - - /* create /proc/irq/1234 */ - irq_dir[irq] = proc_mkdir(name, root_irq_dir); - -#ifdef CONFIG_SMP - /* XXX SMP affinity not supported on starfire yet. */ - if (this_is_starfire == 0) { - struct proc_dir_entry *entry; - - /* create /proc/irq/1234/smp_affinity */ - entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); - - if (entry) { - entry->nlink = 1; - entry->data = (void *)(long)irq; - entry->read_proc = irq_affinity_read_proc; - entry->write_proc = irq_affinity_write_proc; - } - } -#endif -} - -void init_irq_proc (void) -{ - /* create /proc/irq */ - root_irq_dir = proc_mkdir("irq", NULL); -} - diff --git a/arch/sparc64/kernel/isa.c b/arch/sparc64/kernel/isa.c index 30862abee61..0f3aec72ef5 100644 --- a/arch/sparc64/kernel/isa.c +++ b/arch/sparc64/kernel/isa.c @@ -3,6 +3,8 @@ #include <linux/pci.h> #include <linux/slab.h> #include <asm/oplib.h> +#include <asm/prom.h> +#include <asm/of_device.h> #include <asm/isa.h> struct sparc_isa_bridge *isa_chain; @@ -15,23 +17,19 @@ static void __init fatal_err(const char *reason) static void __init report_dev(struct sparc_isa_device *isa_dev, int child) { if (child) - printk(" (%s)", isa_dev->prom_name); + printk(" (%s)", isa_dev->prom_node->name); else - printk(" [%s", isa_dev->prom_name); + printk(" [%s", isa_dev->prom_node->name); } -static void __init isa_dev_get_resource(struct sparc_isa_device *isa_dev, - struct linux_prom_registers *pregs, - int pregs_size) +static struct linux_prom_registers * __init +isa_dev_get_resource(struct sparc_isa_device *isa_dev) { + struct linux_prom_registers *pregs; unsigned long base, len; int prop_len; - prop_len = prom_getproperty(isa_dev->prom_node, "reg", - (char *) pregs, pregs_size); - - if (prop_len <= 0) - return; + pregs = of_get_property(isa_dev->prom_node, "reg", &prop_len); /* Only the first one is interesting. */ len = pregs[0].reg_size; @@ -42,115 +40,37 @@ static void __init isa_dev_get_resource(struct sparc_isa_device *isa_dev, isa_dev->resource.start = base; isa_dev->resource.end = (base + len - 1UL); isa_dev->resource.flags = IORESOURCE_IO; - isa_dev->resource.name = isa_dev->prom_name; + isa_dev->resource.name = isa_dev->prom_node->name; request_resource(&isa_dev->bus->parent->io_space, &isa_dev->resource); -} -/* I can't believe they didn't put a real INO in the isa device - * interrupts property. The whole point of the OBP properties - * is to shield the kernel from IRQ routing details. - * - * The P1275 standard for ISA devices seems to also have been - * totally ignored. - * - * On later systems, an interrupt-map and interrupt-map-mask scheme - * akin to EBUS is used. - */ -static struct { - int obp_irq; - int pci_ino; -} grover_irq_table[] = { - { 1, 0x00 }, /* dma, unknown ino at this point */ - { 2, 0x27 }, /* floppy */ - { 3, 0x22 }, /* parallel */ - { 4, 0x2b }, /* serial */ - { 5, 0x25 }, /* acpi power management */ - - { 0, 0x00 } /* end of table */ -}; - -static int __init isa_dev_get_irq_using_imap(struct sparc_isa_device *isa_dev, - struct sparc_isa_bridge *isa_br, - int *interrupt, - struct linux_prom_registers *pregs) -{ - unsigned int hi, lo, irq; - int i; - - hi = pregs->which_io & isa_br->isa_intmask.phys_hi; - lo = pregs->phys_addr & isa_br->isa_intmask.phys_lo; - irq = *interrupt & isa_br->isa_intmask.interrupt; - for (i = 0; i < isa_br->num_isa_intmap; i++) { - if ((isa_br->isa_intmap[i].phys_hi == hi) && - (isa_br->isa_intmap[i].phys_lo == lo) && - (isa_br->isa_intmap[i].interrupt == irq)) { - *interrupt = isa_br->isa_intmap[i].cinterrupt; - return 0; - } - } - return -1; + return pregs; } static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev, struct linux_prom_registers *pregs) { - int irq_prop; + struct of_device *op = of_find_device_by_node(isa_dev->prom_node); - irq_prop = prom_getintdefault(isa_dev->prom_node, - "interrupts", -1); - if (irq_prop <= 0) { - goto no_irq; + if (!op || !op->num_irqs) { + isa_dev->irq = PCI_IRQ_NONE; } else { - struct pci_controller_info *pcic; - struct pci_pbm_info *pbm; - int i; - - if (isa_dev->bus->num_isa_intmap) { - if (!isa_dev_get_irq_using_imap(isa_dev, - isa_dev->bus, - &irq_prop, - pregs)) - goto route_irq; - } - - for (i = 0; grover_irq_table[i].obp_irq != 0; i++) { - if (grover_irq_table[i].obp_irq == irq_prop) { - int ino = grover_irq_table[i].pci_ino; - - if (ino == 0) - goto no_irq; - - irq_prop = ino; - goto route_irq; - } - } - goto no_irq; - -route_irq: - pbm = isa_dev->bus->parent; - pcic = pbm->parent; - isa_dev->irq = pcic->irq_build(pbm, NULL, irq_prop); - return; + isa_dev->irq = op->irqs[0]; } - -no_irq: - isa_dev->irq = PCI_IRQ_NONE; } static void __init isa_fill_children(struct sparc_isa_device *parent_isa_dev) { - int node = prom_getchild(parent_isa_dev->prom_node); + struct device_node *dp = parent_isa_dev->prom_node->child; - if (node == 0) + if (!dp) return; printk(" ->"); - while (node != 0) { - struct linux_prom_registers regs[PROMREG_MAX]; + while (dp) { + struct linux_prom_registers *regs; struct sparc_isa_device *isa_dev; - int prop_len; isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL); if (!isa_dev) { @@ -165,49 +85,46 @@ static void __init isa_fill_children(struct sparc_isa_device *parent_isa_dev) parent_isa_dev->child = isa_dev; isa_dev->bus = parent_isa_dev->bus; - isa_dev->prom_node = node; - prop_len = prom_getproperty(node, "name", - (char *) isa_dev->prom_name, - sizeof(isa_dev->prom_name)); - if (prop_len <= 0) { - fatal_err("cannot get child isa_dev OBP node name"); - prom_halt(); - } - - prop_len = prom_getproperty(node, "compatible", - (char *) isa_dev->compatible, - sizeof(isa_dev->compatible)); - - /* Not having this is OK. */ - if (prop_len <= 0) - isa_dev->compatible[0] = '\0'; + isa_dev->prom_node = dp; - isa_dev_get_resource(isa_dev, regs, sizeof(regs)); + regs = isa_dev_get_resource(isa_dev); isa_dev_get_irq(isa_dev, regs); report_dev(isa_dev, 1); - node = prom_getsibling(node); + dp = dp->sibling; } } static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br) { - int node = prom_getchild(isa_br->prom_node); + struct device_node *dp = isa_br->prom_node->child; - while (node != 0) { - struct linux_prom_registers regs[PROMREG_MAX]; + while (dp) { + struct linux_prom_registers *regs; struct sparc_isa_device *isa_dev; - int prop_len; isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL); if (!isa_dev) { - fatal_err("cannot allocate isa_dev"); - prom_halt(); + printk(KERN_DEBUG "ISA: cannot allocate isa_dev"); + return; } memset(isa_dev, 0, sizeof(*isa_dev)); + isa_dev->ofdev.node = dp; + isa_dev->ofdev.dev.parent = &isa_br->ofdev.dev; + isa_dev->ofdev.dev.bus = &isa_bus_type; + strcpy(isa_dev->ofdev.dev.bus_id, dp->path_component_name); + + /* Register with core */ + if (of_device_register(&isa_dev->ofdev) != 0) { + printk(KERN_DEBUG "isa: device registration error for %s!\n", + isa_dev->ofdev.dev.bus_id); + kfree(isa_dev); + goto next_sibling; + } + /* Link it in. */ isa_dev->next = NULL; if (isa_br->devices == NULL) { @@ -222,24 +139,9 @@ static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br) } isa_dev->bus = isa_br; - isa_dev->prom_node = node; - prop_len = prom_getproperty(node, "name", - (char *) isa_dev->prom_name, - sizeof(isa_dev->prom_name)); - if (prop_len <= 0) { - fatal_err("cannot get isa_dev OBP node name"); - prom_halt(); - } - - prop_len = prom_getproperty(node, "compatible", - (char *) isa_dev->compatible, - sizeof(isa_dev->compatible)); - - /* Not having this is OK. */ - if (prop_len <= 0) - isa_dev->compatible[0] = '\0'; + isa_dev->prom_node = dp; - isa_dev_get_resource(isa_dev, regs, sizeof(regs)); + regs = isa_dev_get_resource(isa_dev); isa_dev_get_irq(isa_dev, regs); report_dev(isa_dev, 0); @@ -248,7 +150,8 @@ static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br) printk("]"); - node = prom_getsibling(node); + next_sibling: + dp = dp->sibling; } } @@ -266,7 +169,7 @@ void __init isa_init(void) struct pcidev_cookie *pdev_cookie; struct pci_pbm_info *pbm; struct sparc_isa_bridge *isa_br; - int prop_len; + struct device_node *dp; pdev_cookie = pdev->sysdata; if (!pdev_cookie) { @@ -275,15 +178,29 @@ void __init isa_init(void) continue; } pbm = pdev_cookie->pbm; + dp = pdev_cookie->prom_node; isa_br = kmalloc(sizeof(*isa_br), GFP_KERNEL); if (!isa_br) { - fatal_err("cannot allocate sparc_isa_bridge"); - prom_halt(); + printk(KERN_DEBUG "isa: cannot allocate sparc_isa_bridge"); + return; } memset(isa_br, 0, sizeof(*isa_br)); + isa_br->ofdev.node = dp; + isa_br->ofdev.dev.parent = &pdev->dev; + isa_br->ofdev.dev.bus = &isa_bus_type; + strcpy(isa_br->ofdev.dev.bus_id, dp->path_component_name); + + /* Register with core */ + if (of_device_register(&isa_br->ofdev) != 0) { + printk(KERN_DEBUG "isa: device registration error for %s!\n", + isa_br->ofdev.dev.bus_id); + kfree(isa_br); + return; + } + /* Link it in. */ isa_br->next = isa_chain; isa_chain = isa_br; @@ -292,33 +209,6 @@ void __init isa_init(void) isa_br->self = pdev; isa_br->index = index++; isa_br->prom_node = pdev_cookie->prom_node; - strncpy(isa_br->prom_name, pdev_cookie->prom_name, - sizeof(isa_br->prom_name)); - - prop_len = prom_getproperty(isa_br->prom_node, - "ranges", - (char *) isa_br->isa_ranges, - sizeof(isa_br->isa_ranges)); - if (prop_len <= 0) - isa_br->num_isa_ranges = 0; - else - isa_br->num_isa_ranges = - (prop_len / sizeof(struct linux_prom_isa_ranges)); - - prop_len = prom_getproperty(isa_br->prom_node, - "interrupt-map", - (char *) isa_br->isa_intmap, - sizeof(isa_br->isa_intmap)); - if (prop_len <= 0) - isa_br->num_isa_intmap = 0; - else - isa_br->num_isa_intmap = - (prop_len / sizeof(struct linux_prom_isa_intmap)); - - prop_len = prom_getproperty(isa_br->prom_node, - "interrupt-map-mask", - (char *) &(isa_br->isa_intmask), - sizeof(isa_br->isa_intmask)); printk("isa%d:", isa_br->index); diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c index 2e1c824c1cc..8e75ed762fd 100644 --- a/arch/sparc64/kernel/kprobes.c +++ b/arch/sparc64/kernel/kprobes.c @@ -3,7 +3,6 @@ * Copyright (C) 2004 David S. Miller <davem@davemloft.net> */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/kprobes.h> #include <linux/module.h> diff --git a/arch/sparc64/kernel/ktlb.S b/arch/sparc64/kernel/ktlb.S index 31da1e564c9..e492db845ea 100644 --- a/arch/sparc64/kernel/ktlb.S +++ b/arch/sparc64/kernel/ktlb.S @@ -6,7 +6,6 @@ * Copyright (C) 1996,98,99 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include <linux/config.h> #include <asm/head.h> #include <asm/asi.h> #include <asm/page.h> diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c new file mode 100644 index 00000000000..169b017eec0 --- /dev/null +++ b/arch/sparc64/kernel/of_device.c @@ -0,0 +1,970 @@ +#include <linux/config.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/slab.h> + +#include <asm/errno.h> +#include <asm/of_device.h> + +/** + * of_match_device - Tell if an of_device structure has a matching + * of_match structure + * @ids: array of of device match structures to search in + * @dev: the of device structure to match against + * + * Used by a driver to check whether an of_device present in the + * system is in its list of supported devices. + */ +const struct of_device_id *of_match_device(const struct of_device_id *matches, + const struct of_device *dev) +{ + if (!dev->node) + return NULL; + while (matches->name[0] || matches->type[0] || matches->compatible[0]) { + int match = 1; + if (matches->name[0]) + match &= dev->node->name + && !strcmp(matches->name, dev->node->name); + if (matches->type[0]) + match &= dev->node->type + && !strcmp(matches->type, dev->node->type); + if (matches->compatible[0]) + match &= of_device_is_compatible(dev->node, + matches->compatible); + if (match) + return matches; + matches++; + } + return NULL; +} + +static int of_platform_bus_match(struct device *dev, struct device_driver *drv) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * of_drv = to_of_platform_driver(drv); + const struct of_device_id * matches = of_drv->match_table; + + if (!matches) + return 0; + + return of_match_device(matches, of_dev) != NULL; +} + +struct of_device *of_dev_get(struct of_device *dev) +{ + struct device *tmp; + + if (!dev) + return NULL; + tmp = get_device(&dev->dev); + if (tmp) + return to_of_device(tmp); + else + return NULL; +} + +void of_dev_put(struct of_device *dev) +{ + if (dev) + put_device(&dev->dev); +} + + +static int of_device_probe(struct device *dev) +{ + int error = -ENODEV; + struct of_platform_driver *drv; + struct of_device *of_dev; + const struct of_device_id *match; + + drv = to_of_platform_driver(dev->driver); + of_dev = to_of_device(dev); + + if (!drv->probe) + return error; + + of_dev_get(of_dev); + + match = of_match_device(drv->match_table, of_dev); + if (match) + error = drv->probe(of_dev, match); + if (error) + of_dev_put(of_dev); + + return error; +} + +static int of_device_remove(struct device *dev) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + + if (dev->driver && drv->remove) + drv->remove(of_dev); + return 0; +} + +static int of_device_suspend(struct device *dev, pm_message_t state) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + int error = 0; + + if (dev->driver && drv->suspend) + error = drv->suspend(of_dev, state); + return error; +} + +static int of_device_resume(struct device * dev) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + int error = 0; + + if (dev->driver && drv->resume) + error = drv->resume(of_dev); + return error; +} + +void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name) +{ + unsigned long ret = res->start + offset; + + if (!request_region(ret, size, name)) + ret = 0; + + return (void __iomem *) ret; +} +EXPORT_SYMBOL(of_ioremap); + +void of_iounmap(void __iomem *base, unsigned long size) +{ + release_region((unsigned long) base, size); +} +EXPORT_SYMBOL(of_iounmap); + +static int node_match(struct device *dev, void *data) +{ + struct of_device *op = to_of_device(dev); + struct device_node *dp = data; + + return (op->node == dp); +} + +struct of_device *of_find_device_by_node(struct device_node *dp) +{ + struct device *dev = bus_find_device(&of_bus_type, NULL, + dp, node_match); + + if (dev) + return to_of_device(dev); + + return NULL; +} +EXPORT_SYMBOL(of_find_device_by_node); + +#ifdef CONFIG_PCI +struct bus_type isa_bus_type = { + .name = "isa", + .match = of_platform_bus_match, + .probe = of_device_probe, + .remove = of_device_remove, + .suspend = of_device_suspend, + .resume = of_device_resume, +}; +EXPORT_SYMBOL(isa_bus_type); + +struct bus_type ebus_bus_type = { + .name = "ebus", + .match = of_platform_bus_match, + .probe = of_device_probe, + .remove = of_device_remove, + .suspend = of_device_suspend, + .resume = of_device_resume, +}; +EXPORT_SYMBOL(ebus_bus_type); +#endif + +#ifdef CONFIG_SBUS +struct bus_type sbus_bus_type = { + .name = "sbus", + .match = of_platform_bus_match, + .probe = of_device_probe, + .remove = of_device_remove, + .suspend = of_device_suspend, + .resume = of_device_resume, +}; +EXPORT_SYMBOL(sbus_bus_type); +#endif + +struct bus_type of_bus_type = { + .name = "of", + .match = of_platform_bus_match, + .probe = of_device_probe, + .remove = of_device_remove, + .suspend = of_device_suspend, + .resume = of_device_resume, +}; +EXPORT_SYMBOL(of_bus_type); + +static inline u64 of_read_addr(u32 *cell, int size) +{ + u64 r = 0; + while (size--) + r = (r << 32) | *(cell++); + return r; +} + +static void __init get_cells(struct device_node *dp, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = of_n_addr_cells(dp); + if (sizec) + *sizec = of_n_size_cells(dp); +} + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 + +struct of_bus { + const char *name; + const char *addr_prop_name; + int (*match)(struct device_node *parent); + void (*count_cells)(struct device_node *child, + int *addrc, int *sizec); + u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna); + int (*translate)(u32 *addr, u64 offset, int na); + unsigned int (*get_flags)(u32 *addr); +}; + +/* + * Default translator (generic bus) + */ + +static void of_bus_default_count_cells(struct device_node *dev, + int *addrc, int *sizec) +{ + get_cells(dev, addrc, sizec); +} + +static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = of_read_addr(range, na); + s = of_read_addr(range + na + pna, ns); + da = of_read_addr(addr, na); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_default_translate(u32 *addr, u64 offset, int na) +{ + u64 a = of_read_addr(addr, na); + memset(addr, 0, na * 4); + a += offset; + if (na > 1) + addr[na - 2] = a >> 32; + addr[na - 1] = a & 0xffffffffu; + + return 0; +} + +static unsigned int of_bus_default_get_flags(u32 *addr) +{ + return IORESOURCE_MEM; +} + +/* + * PCI bus specific translator + */ + +static int of_bus_pci_match(struct device_node *np) +{ + return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex"); +} + +static void of_bus_pci_count_cells(struct device_node *np, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = 3; + if (sizec) + *sizec = 2; +} + +static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna) +{ + u64 cp, s, da; + + /* Check address type match */ + if ((addr[0] ^ range[0]) & 0x03000000) + return OF_BAD_ADDR; + + /* Read address values, skipping high cell */ + cp = of_read_addr(range + 1, na - 1); + s = of_read_addr(range + na + pna, ns); + da = of_read_addr(addr + 1, na - 1); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_pci_translate(u32 *addr, u64 offset, int na) +{ + return of_bus_default_translate(addr + 1, offset, na - 1); +} + +static unsigned int of_bus_pci_get_flags(u32 *addr) +{ + unsigned int flags = 0; + u32 w = addr[0]; + + switch((w >> 24) & 0x03) { + case 0x01: + flags |= IORESOURCE_IO; + case 0x02: /* 32 bits */ + case 0x03: /* 64 bits */ + flags |= IORESOURCE_MEM; + } + if (w & 0x40000000) + flags |= IORESOURCE_PREFETCH; + return flags; +} + +/* + * ISA bus specific translator + */ + +static int of_bus_isa_match(struct device_node *np) +{ + return !strcmp(np->name, "isa"); +} + +static void of_bus_isa_count_cells(struct device_node *child, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = 2; + if (sizec) + *sizec = 1; +} + +static u64 of_bus_isa_map(u32 *addr, u32 *range, int na, int ns, int pna) +{ + u64 cp, s, da; + + /* Check address type match */ + if ((addr[0] ^ range[0]) & 0x00000001) + return OF_BAD_ADDR; + + /* Read address values, skipping high cell */ + cp = of_read_addr(range + 1, na - 1); + s = of_read_addr(range + na + pna, ns); + da = of_read_addr(addr + 1, na - 1); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_isa_translate(u32 *addr, u64 offset, int na) +{ + return of_bus_default_translate(addr + 1, offset, na - 1); +} + +static unsigned int of_bus_isa_get_flags(u32 *addr) +{ + unsigned int flags = 0; + u32 w = addr[0]; + + if (w & 1) + flags |= IORESOURCE_IO; + else + flags |= IORESOURCE_MEM; + return flags; +} + +/* + * SBUS bus specific translator + */ + +static int of_bus_sbus_match(struct device_node *np) +{ + return !strcmp(np->name, "sbus") || + !strcmp(np->name, "sbi"); +} + +static void of_bus_sbus_count_cells(struct device_node *child, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = 2; + if (sizec) + *sizec = 1; +} + +static u64 of_bus_sbus_map(u32 *addr, u32 *range, int na, int ns, int pna) +{ + return of_bus_default_map(addr, range, na, ns, pna); +} + +static int of_bus_sbus_translate(u32 *addr, u64 offset, int na) +{ + return of_bus_default_translate(addr, offset, na); +} + +static unsigned int of_bus_sbus_get_flags(u32 *addr) +{ + return IORESOURCE_MEM; +} + + +/* + * Array of bus specific translators + */ + +static struct of_bus of_busses[] = { + /* PCI */ + { + .name = "pci", + .addr_prop_name = "assigned-addresses", + .match = of_bus_pci_match, + .count_cells = of_bus_pci_count_cells, + .map = of_bus_pci_map, + .translate = of_bus_pci_translate, + .get_flags = of_bus_pci_get_flags, + }, + /* ISA */ + { + .name = "isa", + .addr_prop_name = "reg", + .match = of_bus_isa_match, + .count_cells = of_bus_isa_count_cells, + .map = of_bus_isa_map, + .translate = of_bus_isa_translate, + .get_flags = of_bus_isa_get_flags, + }, + /* SBUS */ + { + .name = "sbus", + .addr_prop_name = "reg", + .match = of_bus_sbus_match, + .count_cells = of_bus_sbus_count_cells, + .map = of_bus_sbus_map, + .translate = of_bus_sbus_translate, + .get_flags = of_bus_sbus_get_flags, + }, + /* Default */ + { + .name = "default", + .addr_prop_name = "reg", + .match = NULL, + .count_cells = of_bus_default_count_cells, + .map = of_bus_default_map, + .translate = of_bus_default_translate, + .get_flags = of_bus_default_get_flags, + }, +}; + +static struct of_bus *of_match_bus(struct device_node *np) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(of_busses); i ++) + if (!of_busses[i].match || of_busses[i].match(np)) + return &of_busses[i]; + BUG(); + return NULL; +} + +static int __init build_one_resource(struct device_node *parent, + struct of_bus *bus, + struct of_bus *pbus, + u32 *addr, + int na, int ns, int pna) +{ + u32 *ranges; + unsigned int rlen; + int rone; + u64 offset = OF_BAD_ADDR; + + ranges = of_get_property(parent, "ranges", &rlen); + if (ranges == NULL || rlen == 0) { + offset = of_read_addr(addr, na); + memset(addr, 0, pna * 4); + goto finish; + } + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for (; rlen >= rone; rlen -= rone, ranges += rone) { + offset = bus->map(addr, ranges, na, ns, pna); + if (offset != OF_BAD_ADDR) + break; + } + if (offset == OF_BAD_ADDR) + return 1; + + memcpy(addr, ranges + na, 4 * pna); + +finish: + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +static void __init build_device_resources(struct of_device *op, + struct device *parent) +{ + struct of_device *p_op; + struct of_bus *bus; + int na, ns; + int index, num_reg; + void *preg; + + if (!parent) + return; + + p_op = to_of_device(parent); + bus = of_match_bus(p_op->node); + bus->count_cells(op->node, &na, &ns); + + preg = of_get_property(op->node, bus->addr_prop_name, &num_reg); + if (!preg || num_reg == 0) + return; + + /* Convert to num-cells. */ + num_reg /= 4; + + /* Conver to num-entries. */ + num_reg /= na + ns; + + for (index = 0; index < num_reg; index++) { + struct resource *r = &op->resource[index]; + u32 addr[OF_MAX_ADDR_CELLS]; + u32 *reg = (preg + (index * ((na + ns) * 4))); + struct device_node *dp = op->node; + struct device_node *pp = p_op->node; + struct of_bus *pbus; + u64 size, result = OF_BAD_ADDR; + unsigned long flags; + int dna, dns; + int pna, pns; + + size = of_read_addr(reg + na, ns); + flags = bus->get_flags(reg); + + memcpy(addr, reg, na * 4); + + /* If the immediate parent has no ranges property to apply, + * just use a 1<->1 mapping. Unless it is the 'dma' child + * of an isa bus, which must be passed up towards the root. + * + * Also, don't try to translate PMU bus device registers. + */ + if ((of_find_property(pp, "ranges", NULL) == NULL && + strcmp(pp->name, "dma") != 0) || + !strcmp(pp->name, "pmu")) { + result = of_read_addr(addr, na); + goto build_res; + } + + dna = na; + dns = ns; + + while (1) { + dp = pp; + pp = dp->parent; + if (!pp) { + result = of_read_addr(addr, dna); + break; + } + + pbus = of_match_bus(pp); + pbus->count_cells(dp, &pna, &pns); + + if (build_one_resource(dp, bus, pbus, addr, dna, dns, pna)) + break; + + dna = pna; + dns = pns; + bus = pbus; + } + + build_res: + memset(r, 0, sizeof(*r)); + if (result != OF_BAD_ADDR) { + if (tlb_type == hypervisor) + result &= 0x0fffffffffffffffUL; + + r->start = result; + r->end = result + size - 1; + r->flags = flags; + } else { + r->start = ~0UL; + r->end = ~0UL; + } + r->name = op->node->name; + } +} + +static struct device_node * __init +apply_interrupt_map(struct device_node *dp, struct device_node *pp, + u32 *imap, int imlen, u32 *imask, + unsigned int *irq_p) +{ + struct device_node *cp; + unsigned int irq = *irq_p; + struct of_bus *bus; + phandle handle; + u32 *reg; + int na, num_reg, i; + + bus = of_match_bus(pp); + bus->count_cells(dp, &na, NULL); + + reg = of_get_property(dp, "reg", &num_reg); + if (!reg || !num_reg) + return NULL; + + imlen /= ((na + 3) * 4); + handle = 0; + for (i = 0; i < imlen; i++) { + int j; + + for (j = 0; j < na; j++) { + if ((reg[j] & imask[j]) != imap[j]) + goto next; + } + if (imap[na] == irq) { + handle = imap[na + 1]; + irq = imap[na + 2]; + break; + } + + next: + imap += (na + 3); + } + if (i == imlen) + return NULL; + + *irq_p = irq; + cp = of_find_node_by_phandle(handle); + + return cp; +} + +static unsigned int __init pci_irq_swizzle(struct device_node *dp, + struct device_node *pp, + unsigned int irq) +{ + struct linux_prom_pci_registers *regs; + unsigned int devfn, slot, ret; + + if (irq < 1 || irq > 4) + return irq; + + regs = of_get_property(dp, "reg", NULL); + if (!regs) + return irq; + + devfn = (regs->phys_hi >> 8) & 0xff; + slot = (devfn >> 3) & 0x1f; + + ret = ((irq - 1 + (slot & 3)) & 3) + 1; + + return ret; +} + +static unsigned int __init build_one_device_irq(struct of_device *op, + struct device *parent, + unsigned int irq) +{ + struct device_node *dp = op->node; + struct device_node *pp, *ip; + unsigned int orig_irq = irq; + + if (irq == 0xffffffff) + return irq; + + if (dp->irq_trans) { + irq = dp->irq_trans->irq_build(dp, irq, + dp->irq_trans->data); +#if 1 + printk("%s: direct translate %x --> %x\n", + dp->full_name, orig_irq, irq); +#endif + return irq; + } + + /* Something more complicated. Walk up to the root, applying + * interrupt-map or bus specific translations, until we hit + * an IRQ translator. + * + * If we hit a bus type or situation we cannot handle, we + * stop and assume that the original IRQ number was in a + * format which has special meaning to it's immediate parent. + */ + pp = dp->parent; + ip = NULL; + while (pp) { + void *imap, *imsk; + int imlen; + + imap = of_get_property(pp, "interrupt-map", &imlen); + imsk = of_get_property(pp, "interrupt-map-mask", NULL); + if (imap && imsk) { + struct device_node *iret; + int this_orig_irq = irq; + + iret = apply_interrupt_map(dp, pp, + imap, imlen, imsk, + &irq); +#if 1 + printk("%s: Apply [%s:%x] imap --> [%s:%x]\n", + op->node->full_name, + pp->full_name, this_orig_irq, + (iret ? iret->full_name : "NULL"), irq); +#endif + if (!iret) + break; + + if (iret->irq_trans) { + ip = iret; + break; + } + } else { + if (!strcmp(pp->type, "pci") || + !strcmp(pp->type, "pciex")) { + unsigned int this_orig_irq = irq; + + irq = pci_irq_swizzle(dp, pp, irq); +#if 1 + printk("%s: PCI swizzle [%s] %x --> %x\n", + op->node->full_name, + pp->full_name, this_orig_irq, irq); +#endif + } + + if (pp->irq_trans) { + ip = pp; + break; + } + } + dp = pp; + pp = pp->parent; + } + if (!ip) + return orig_irq; + + irq = ip->irq_trans->irq_build(op->node, irq, + ip->irq_trans->data); +#if 1 + printk("%s: Apply IRQ trans [%s] %x --> %x\n", + op->node->full_name, ip->full_name, orig_irq, irq); +#endif + + return irq; +} + +static struct of_device * __init scan_one_device(struct device_node *dp, + struct device *parent) +{ + struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL); + unsigned int *irq; + int len, i; + + if (!op) + return NULL; + + op->node = dp; + + op->clock_freq = of_getintprop_default(dp, "clock-frequency", + (25*1000*1000)); + op->portid = of_getintprop_default(dp, "upa-portid", -1); + if (op->portid == -1) + op->portid = of_getintprop_default(dp, "portid", -1); + + irq = of_get_property(dp, "interrupts", &len); + if (irq) { + memcpy(op->irqs, irq, len); + op->num_irqs = len / 4; + } else { + op->num_irqs = 0; + } + + build_device_resources(op, parent); + for (i = 0; i < op->num_irqs; i++) + op->irqs[i] = build_one_device_irq(op, parent, op->irqs[i]); + + op->dev.parent = parent; + op->dev.bus = &of_bus_type; + if (!parent) + strcpy(op->dev.bus_id, "root"); + else + strcpy(op->dev.bus_id, dp->path_component_name); + + if (of_device_register(op)) { + printk("%s: Could not register of device.\n", + dp->full_name); + kfree(op); + op = NULL; + } + + return op; +} + +static void __init scan_tree(struct device_node *dp, struct device *parent) +{ + while (dp) { + struct of_device *op = scan_one_device(dp, parent); + + if (op) + scan_tree(dp->child, &op->dev); + + dp = dp->sibling; + } +} + +static void __init scan_of_devices(void) +{ + struct device_node *root = of_find_node_by_path("/"); + struct of_device *parent; + + parent = scan_one_device(root, NULL); + if (!parent) + return; + + scan_tree(root->child, &parent->dev); +} + +static int __init of_bus_driver_init(void) +{ + int err; + + err = bus_register(&of_bus_type); +#ifdef CONFIG_PCI + if (!err) + err = bus_register(&isa_bus_type); + if (!err) + err = bus_register(&ebus_bus_type); +#endif +#ifdef CONFIG_SBUS + if (!err) + err = bus_register(&sbus_bus_type); +#endif + + if (!err) + scan_of_devices(); + + return err; +} + +postcore_initcall(of_bus_driver_init); + +int of_register_driver(struct of_platform_driver *drv, struct bus_type *bus) +{ + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = bus; + + /* register with core */ + return driver_register(&drv->driver); +} + +void of_unregister_driver(struct of_platform_driver *drv) +{ + driver_unregister(&drv->driver); +} + + +static ssize_t dev_show_devspec(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct of_device *ofdev; + + ofdev = to_of_device(dev); + return sprintf(buf, "%s", ofdev->node->full_name); +} + +static DEVICE_ATTR(devspec, S_IRUGO, dev_show_devspec, NULL); + +/** + * of_release_dev - free an of device structure when all users of it are finished. + * @dev: device that's been disconnected + * + * Will be called only by the device core when all users of this of device are + * done. + */ +void of_release_dev(struct device *dev) +{ + struct of_device *ofdev; + + ofdev = to_of_device(dev); + + kfree(ofdev); +} + +int of_device_register(struct of_device *ofdev) +{ + int rc; + + BUG_ON(ofdev->node == NULL); + + rc = device_register(&ofdev->dev); + if (rc) + return rc; + + device_create_file(&ofdev->dev, &dev_attr_devspec); + + return 0; +} + +void of_device_unregister(struct of_device *ofdev) +{ + device_remove_file(&ofdev->dev, &dev_attr_devspec); + device_unregister(&ofdev->dev); +} + +struct of_device* of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent, + struct bus_type *bus) +{ + struct of_device *dev; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + memset(dev, 0, sizeof(*dev)); + + dev->dev.parent = parent; + dev->dev.bus = bus; + dev->dev.release = of_release_dev; + + strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE); + + if (of_device_register(dev) != 0) { + kfree(dev); + return NULL; + } + + return dev; +} + +EXPORT_SYMBOL(of_match_device); +EXPORT_SYMBOL(of_register_driver); +EXPORT_SYMBOL(of_unregister_driver); +EXPORT_SYMBOL(of_device_register); +EXPORT_SYMBOL(of_device_unregister); +EXPORT_SYMBOL(of_dev_get); +EXPORT_SYMBOL(of_dev_put); +EXPORT_SYMBOL(of_platform_device_create); +EXPORT_SYMBOL(of_release_dev); diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index f97ddeb105a..e02f01b644a 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -6,7 +6,6 @@ * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) */ -#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> @@ -22,6 +21,7 @@ #include <asm/irq.h> #include <asm/ebus.h> #include <asm/isa.h> +#include <asm/prom.h> unsigned long pci_memspace_mask = 0xffffffffUL; @@ -47,12 +47,6 @@ struct pci_controller_info *pci_controller_root = NULL; /* Each PCI controller found gets a unique index. */ int pci_num_controllers = 0; -/* At boot time the user can give the kernel a command - * line option which controls if and how PCI devices - * are reordered at PCI bus probing time. - */ -int pci_device_reorder = 0; - volatile int pci_poke_in_progress; volatile int pci_poke_cpu = -1; volatile int pci_poke_faulted; @@ -183,16 +177,16 @@ void pci_config_write32(u32 *addr, u32 val) } /* Probe for all PCI controllers in the system. */ -extern void sabre_init(int, char *); -extern void psycho_init(int, char *); -extern void schizo_init(int, char *); -extern void schizo_plus_init(int, char *); -extern void tomatillo_init(int, char *); -extern void sun4v_pci_init(int, char *); +extern void sabre_init(struct device_node *, const char *); +extern void psycho_init(struct device_node *, const char *); +extern void schizo_init(struct device_node *, const char *); +extern void schizo_plus_init(struct device_node *, const char *); +extern void tomatillo_init(struct device_node *, const char *); +extern void sun4v_pci_init(struct device_node *, const char *); static struct { char *model_name; - void (*init)(int, char *); + void (*init)(struct device_node *, const char *); } pci_controller_table[] __initdata = { { "SUNW,sabre", sabre_init }, { "pci108e,a000", sabre_init }, @@ -210,7 +204,7 @@ static struct { #define PCI_NUM_CONTROLLER_TYPES (sizeof(pci_controller_table) / \ sizeof(pci_controller_table[0])) -static int __init pci_controller_init(char *model_name, int namelen, int node) +static int __init pci_controller_init(const char *model_name, int namelen, struct device_node *dp) { int i; @@ -218,18 +212,15 @@ static int __init pci_controller_init(char *model_name, int namelen, int node) if (!strncmp(model_name, pci_controller_table[i].model_name, namelen)) { - pci_controller_table[i].init(node, model_name); + pci_controller_table[i].init(dp, model_name); return 1; } } - printk("PCI: Warning unknown controller, model name [%s]\n", - model_name); - printk("PCI: Ignoring controller...\n"); return 0; } -static int __init pci_is_controller(char *model_name, int namelen, int node) +static int __init pci_is_controller(const char *model_name, int namelen, struct device_node *dp) { int i; @@ -243,36 +234,35 @@ static int __init pci_is_controller(char *model_name, int namelen, int node) return 0; } -static int __init pci_controller_scan(int (*handler)(char *, int, int)) +static int __init pci_controller_scan(int (*handler)(const char *, int, struct device_node *)) { - char namebuf[64]; - int node; + struct device_node *dp; int count = 0; - node = prom_getchild(prom_root_node); - while ((node = prom_searchsiblings(node, "pci")) != 0) { + for_each_node_by_name(dp, "pci") { + struct property *prop; int len; - if ((len = prom_getproperty(node, "model", namebuf, sizeof(namebuf))) > 0 || - (len = prom_getproperty(node, "compatible", namebuf, sizeof(namebuf))) > 0) { + prop = of_find_property(dp, "model", &len); + if (!prop) + prop = of_find_property(dp, "compatible", &len); + + if (prop) { + const char *model = prop->value; int item_len = 0; /* Our value may be a multi-valued string in the * case of some compatible properties. For sanity, - * only try the first one. */ - - while (namebuf[item_len] && len) { + * only try the first one. + */ + while (model[item_len] && len) { len--; item_len++; } - if (handler(namebuf, item_len, node)) + if (handler(model, item_len, dp)) count++; } - - node = prom_getsibling(node); - if (!node) - break; } return count; @@ -316,28 +306,6 @@ static void __init pci_scan_each_controller_bus(void) p->scan_bus(p); } -/* Reorder the pci_dev chain, so that onboard devices come first - * and then come the pluggable cards. - */ -static void __init pci_reorder_devs(void) -{ - struct list_head *pci_onboard = &pci_devices; - struct list_head *walk = pci_onboard->next; - - while (walk != pci_onboard) { - struct pci_dev *pdev = pci_dev_g(walk); - struct list_head *walk_next = walk->next; - - if (pdev->irq && (__irq_ino(pdev->irq) & 0x20)) { - list_del(walk); - list_add(walk, pci_onboard); - } - - walk = walk_next; - } -} - -extern void clock_probe(void); extern void power_init(void); static int __init pcibios_init(void) @@ -348,12 +316,8 @@ static int __init pcibios_init(void) pci_scan_each_controller_bus(); - if (pci_device_reorder) - pci_reorder_devs(); - isa_init(); ebus_init(); - clock_probe(); power_init(); return 0; @@ -390,7 +354,7 @@ void pcibios_update_irq(struct pci_dev *pdev, int irq) } void pcibios_align_resource(void *data, struct resource *res, - unsigned long size, unsigned long align) + resource_size_t size, resource_size_t align) { } @@ -441,14 +405,6 @@ EXPORT_SYMBOL(pcibios_bus_to_resource); char * __init pcibios_setup(char *str) { - if (!strcmp(str, "onboardfirst")) { - pci_device_reorder = 1; - return NULL; - } - if (!strcmp(str, "noreorder")) { - pci_device_reorder = 0; - return NULL; - } return str; } diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index 33dedb1aacd..7a59cc72c84 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c @@ -9,6 +9,10 @@ #include <linux/init.h> #include <asm/pbm.h> +#include <asm/prom.h> +#include <asm/of_device.h> + +#include "pci_impl.h" /* Fix self device of BUS and hook it into BUS->self. * The pci_scan_bus does not do this for the host bridge. @@ -28,16 +32,14 @@ void __init pci_fixup_host_bridge_self(struct pci_bus *pbus) prom_halt(); } -/* Find the OBP PROM device tree node for a PCI device. - * Return zero if not found. - */ -static int __init find_device_prom_node(struct pci_pbm_info *pbm, - struct pci_dev *pdev, - int bus_prom_node, - struct linux_prom_pci_registers *pregs, - int *nregs) +/* Find the OBP PROM device tree node for a PCI device. */ +static struct device_node * __init +find_device_prom_node(struct pci_pbm_info *pbm, struct pci_dev *pdev, + struct device_node *bus_node, + struct linux_prom_pci_registers **pregs, + int *nregs) { - int node; + struct device_node *dp; *nregs = 0; @@ -54,24 +56,30 @@ static int __init find_device_prom_node(struct pci_pbm_info *pbm, pdev->device == PCI_DEVICE_ID_SUN_TOMATILLO || pdev->device == PCI_DEVICE_ID_SUN_SABRE || pdev->device == PCI_DEVICE_ID_SUN_HUMMINGBIRD)) - return bus_prom_node; - - node = prom_getchild(bus_prom_node); - while (node != 0) { - int err = prom_getproperty(node, "reg", - (char *)pregs, - sizeof(*pregs) * PROMREG_MAX); - if (err == 0 || err == -1) + return bus_node; + + dp = bus_node->child; + while (dp) { + struct linux_prom_pci_registers *regs; + struct property *prop; + int len; + + prop = of_find_property(dp, "reg", &len); + if (!prop) goto do_next_sibling; - if (((pregs[0].phys_hi >> 8) & 0xff) == pdev->devfn) { - *nregs = err / sizeof(*pregs); - return node; + + regs = prop->value; + if (((regs[0].phys_hi >> 8) & 0xff) == pdev->devfn) { + *pregs = regs; + *nregs = len / sizeof(struct linux_prom_pci_registers); + return dp; } do_next_sibling: - node = prom_getsibling(node); + dp = dp->sibling; } - return 0; + + return NULL; } /* Older versions of OBP on PCI systems encode 64-bit MEM @@ -128,15 +136,17 @@ static void __init fixup_obp_assignments(struct pci_dev *pdev, */ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm, struct pci_dev *pdev, - int bus_prom_node) + struct device_node *bus_node) { - struct linux_prom_pci_registers pregs[PROMREG_MAX]; + struct linux_prom_pci_registers *pregs = NULL; struct pcidev_cookie *pcp; - int device_prom_node, nregs, err; + struct device_node *dp; + struct property *prop; + int nregs, len; - device_prom_node = find_device_prom_node(pbm, pdev, bus_prom_node, - pregs, &nregs); - if (device_prom_node == 0) { + dp = find_device_prom_node(pbm, pdev, bus_node, + &pregs, &nregs); + if (!dp) { /* If it is not in the OBP device tree then * there must be a damn good reason for it. * @@ -150,45 +160,44 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm, return; } - pcp = kmalloc(sizeof(*pcp), GFP_ATOMIC); + pcp = kzalloc(sizeof(*pcp), GFP_ATOMIC); if (pcp == NULL) { prom_printf("PCI_COOKIE: Fatal malloc error, aborting...\n"); prom_halt(); } pcp->pbm = pbm; - pcp->prom_node = device_prom_node; - memcpy(pcp->prom_regs, pregs, sizeof(pcp->prom_regs)); + pcp->prom_node = dp; + pcp->op = of_find_device_by_node(dp); + memcpy(pcp->prom_regs, pregs, + nregs * sizeof(struct linux_prom_pci_registers)); pcp->num_prom_regs = nregs; - err = prom_getproperty(device_prom_node, "name", - pcp->prom_name, sizeof(pcp->prom_name)); - if (err > 0) - pcp->prom_name[err] = 0; - else - pcp->prom_name[0] = 0; - - err = prom_getproperty(device_prom_node, - "assigned-addresses", - (char *)pcp->prom_assignments, - sizeof(pcp->prom_assignments)); - if (err == 0 || err == -1) + + /* We can't have the pcidev_cookie assignments be just + * direct pointers into the property value, since they + * are potentially modified by the probing process. + */ + prop = of_find_property(dp, "assigned-addresses", &len); + if (!prop) { pcp->num_prom_assignments = 0; - else + } else { + memcpy(pcp->prom_assignments, prop->value, len); pcp->num_prom_assignments = - (err / sizeof(pcp->prom_assignments[0])); + (len / sizeof(pcp->prom_assignments[0])); + } - if (strcmp(pcp->prom_name, "ebus") == 0) { - struct linux_prom_ebus_ranges erng[PROM_PCIRNG_MAX]; + if (strcmp(dp->name, "ebus") == 0) { + struct linux_prom_ebus_ranges *erng; int iter; /* EBUS is special... */ - err = prom_getproperty(device_prom_node, "ranges", - (char *)&erng[0], sizeof(erng)); - if (err == 0 || err == -1) { + prop = of_find_property(dp, "ranges", &len); + if (!prop) { prom_printf("EBUS: Fatal error, no range property\n"); prom_halt(); } - err = (err / sizeof(erng[0])); - for(iter = 0; iter < err; iter++) { + erng = prop->value; + len = (len / sizeof(erng[0])); + for (iter = 0; iter < len; iter++) { struct linux_prom_ebus_ranges *ep = &erng[iter]; struct linux_prom_pci_registers *ap; @@ -200,7 +209,7 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm, ap->size_hi = 0; ap->size_lo = ep->size; } - pcp->num_prom_assignments = err; + pcp->num_prom_assignments = len; } fixup_obp_assignments(pdev, pcp); @@ -210,7 +219,7 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm, void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus, struct pci_pbm_info *pbm, - int prom_node) + struct device_node *dp) { struct pci_dev *pdev, *pdev_next; struct pci_bus *this_pbus, *pbus_next; @@ -218,7 +227,7 @@ void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus, /* This must be _safe because the cookie fillin routine can delete devices from the tree. */ list_for_each_entry_safe(pdev, pdev_next, &pbus->devices, bus_list) - pdev_cookie_fillin(pbm, pdev, prom_node); + pdev_cookie_fillin(pbm, pdev, dp); list_for_each_entry_safe(this_pbus, pbus_next, &pbus->children, node) { struct pcidev_cookie *pcp = this_pbus->self->sysdata; @@ -241,7 +250,6 @@ static void __init bad_assignment(struct pci_dev *pdev, if (res) prom_printf("PCI: RES[%016lx-->%016lx:(%lx)]\n", res->start, res->end, res->flags); - prom_printf("Please email this information to davem@redhat.com\n"); if (do_prom_halt) prom_halt(); } @@ -273,8 +281,7 @@ __init get_root_resource(struct linux_prom_pci_registers *ap, return &pbm->mem_space; default: - printk("PCI: What is resource space %x? " - "Tell davem@redhat.com about it!\n", space); + printk("PCI: What is resource space %x?\n", space); return NULL; }; } @@ -541,287 +548,18 @@ void __init pci_assign_unassigned(struct pci_pbm_info *pbm, pci_assign_unassigned(pbm, bus); } -static inline unsigned int pci_slot_swivel(struct pci_pbm_info *pbm, - struct pci_dev *toplevel_pdev, - struct pci_dev *pdev, - unsigned int interrupt) -{ - unsigned int ret; - - if (unlikely(interrupt < 1 || interrupt > 4)) { - printk("%s: Device %s interrupt value of %u is strange.\n", - pbm->name, pci_name(pdev), interrupt); - return interrupt; - } - - ret = ((interrupt - 1 + (PCI_SLOT(pdev->devfn) & 3)) & 3) + 1; - - printk("%s: %s IRQ Swivel %s [%x:%x] -> [%x]\n", - pbm->name, pci_name(toplevel_pdev), pci_name(pdev), - interrupt, PCI_SLOT(pdev->devfn), ret); - - return ret; -} - -static inline unsigned int pci_apply_intmap(struct pci_pbm_info *pbm, - struct pci_dev *toplevel_pdev, - struct pci_dev *pbus, - struct pci_dev *pdev, - unsigned int interrupt, - unsigned int *cnode) -{ - struct linux_prom_pci_intmap imap[PROM_PCIIMAP_MAX]; - struct linux_prom_pci_intmask imask; - struct pcidev_cookie *pbus_pcp = pbus->sysdata; - struct pcidev_cookie *pdev_pcp = pdev->sysdata; - struct linux_prom_pci_registers *pregs = pdev_pcp->prom_regs; - int plen, num_imap, i; - unsigned int hi, mid, lo, irq, orig_interrupt; - - *cnode = pbus_pcp->prom_node; - - plen = prom_getproperty(pbus_pcp->prom_node, "interrupt-map", - (char *) &imap[0], sizeof(imap)); - if (plen <= 0 || - (plen % sizeof(struct linux_prom_pci_intmap)) != 0) { - printk("%s: Device %s interrupt-map has bad len %d\n", - pbm->name, pci_name(pbus), plen); - goto no_intmap; - } - num_imap = plen / sizeof(struct linux_prom_pci_intmap); - - plen = prom_getproperty(pbus_pcp->prom_node, "interrupt-map-mask", - (char *) &imask, sizeof(imask)); - if (plen <= 0 || - (plen % sizeof(struct linux_prom_pci_intmask)) != 0) { - printk("%s: Device %s interrupt-map-mask has bad len %d\n", - pbm->name, pci_name(pbus), plen); - goto no_intmap; - } - - orig_interrupt = interrupt; - - hi = pregs->phys_hi & imask.phys_hi; - mid = pregs->phys_mid & imask.phys_mid; - lo = pregs->phys_lo & imask.phys_lo; - irq = interrupt & imask.interrupt; - - for (i = 0; i < num_imap; i++) { - if (imap[i].phys_hi == hi && - imap[i].phys_mid == mid && - imap[i].phys_lo == lo && - imap[i].interrupt == irq) { - *cnode = imap[i].cnode; - interrupt = imap[i].cinterrupt; - } - } - - printk("%s: %s MAP BUS %s DEV %s [%x] -> [%x]\n", - pbm->name, pci_name(toplevel_pdev), - pci_name(pbus), pci_name(pdev), - orig_interrupt, interrupt); - -no_intmap: - return interrupt; -} - -/* For each PCI bus on the way to the root: - * 1) If it has an interrupt-map property, apply it. - * 2) Else, swivel the interrupt number based upon the PCI device number. - * - * Return the "IRQ controller" node. If this is the PBM's device node, - * all interrupt translations are complete, else we should use that node's - * "reg" property to apply the PBM's "interrupt-{map,mask}" to the interrupt. - */ -static unsigned int __init pci_intmap_match_to_root(struct pci_pbm_info *pbm, - struct pci_dev *pdev, - unsigned int *interrupt) -{ - struct pci_dev *toplevel_pdev = pdev; - struct pcidev_cookie *toplevel_pcp = toplevel_pdev->sysdata; - unsigned int cnode = toplevel_pcp->prom_node; - - while (pdev->bus->number != pbm->pci_first_busno) { - struct pci_dev *pbus = pdev->bus->self; - struct pcidev_cookie *pcp = pbus->sysdata; - int plen; - - plen = prom_getproplen(pcp->prom_node, "interrupt-map"); - if (plen <= 0) { - *interrupt = pci_slot_swivel(pbm, toplevel_pdev, - pdev, *interrupt); - cnode = pcp->prom_node; - } else { - *interrupt = pci_apply_intmap(pbm, toplevel_pdev, - pbus, pdev, - *interrupt, &cnode); - - while (pcp->prom_node != cnode && - pbus->bus->number != pbm->pci_first_busno) { - pbus = pbus->bus->self; - pcp = pbus->sysdata; - } - } - pdev = pbus; - - if (cnode == pbm->prom_node) - break; - } - - return cnode; -} - -static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt) -{ - struct pcidev_cookie *dev_pcp = pdev->sysdata; - struct pci_pbm_info *pbm = dev_pcp->pbm; - struct linux_prom_pci_registers reg[PROMREG_MAX]; - unsigned int hi, mid, lo, irq; - int i, cnode, plen; - - cnode = pci_intmap_match_to_root(pbm, pdev, interrupt); - if (cnode == pbm->prom_node) - goto success; - - plen = prom_getproperty(cnode, "reg", (char *) reg, sizeof(reg)); - if (plen <= 0 || - (plen % sizeof(struct linux_prom_pci_registers)) != 0) { - printk("%s: OBP node %x reg property has bad len %d\n", - pbm->name, cnode, plen); - goto fail; - } - - hi = reg[0].phys_hi & pbm->pbm_intmask.phys_hi; - mid = reg[0].phys_mid & pbm->pbm_intmask.phys_mid; - lo = reg[0].phys_lo & pbm->pbm_intmask.phys_lo; - irq = *interrupt & pbm->pbm_intmask.interrupt; - - for (i = 0; i < pbm->num_pbm_intmap; i++) { - struct linux_prom_pci_intmap *intmap; - - intmap = &pbm->pbm_intmap[i]; - - if (intmap->phys_hi == hi && - intmap->phys_mid == mid && - intmap->phys_lo == lo && - intmap->interrupt == irq) { - *interrupt = intmap->cinterrupt; - goto success; - } - } - -fail: - return 0; - -success: - printk("PCI-IRQ: Routing bus[%2x] slot[%2x] to INO[%02x]\n", - pdev->bus->number, PCI_SLOT(pdev->devfn), - *interrupt); - return 1; -} - static void __init pdev_fixup_irq(struct pci_dev *pdev) { struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; - struct pci_controller_info *p = pbm->parent; - unsigned int portid = pbm->portid; - unsigned int prom_irq; - int prom_node = pcp->prom_node; - int err; - - /* If this is an empty EBUS device, sometimes OBP fails to - * give it a valid fully specified interrupts property. - * The EBUS hooked up to SunHME on PCI I/O boards of - * Ex000 systems is one such case. - * - * The interrupt is not important so just ignore it. - */ - if (pdev->vendor == PCI_VENDOR_ID_SUN && - pdev->device == PCI_DEVICE_ID_SUN_EBUS && - !prom_getchild(prom_node)) { - pdev->irq = 0; - return; - } + struct of_device *op = pcp->op; - err = prom_getproperty(prom_node, "interrupts", - (char *)&prom_irq, sizeof(prom_irq)); - if (err == 0 || err == -1) { - pdev->irq = 0; + if (op->irqs[0] == 0xffffffff) { + pdev->irq = PCI_IRQ_NONE; return; } - if (tlb_type != hypervisor) { - /* Fully specified already? */ - if (((prom_irq & PCI_IRQ_IGN) >> 6) == portid) { - pdev->irq = p->irq_build(pbm, pdev, prom_irq); - goto have_irq; - } - - /* An onboard device? (bit 5 set) */ - if ((prom_irq & PCI_IRQ_INO) & 0x20) { - pdev->irq = p->irq_build(pbm, pdev, (portid << 6 | prom_irq)); - goto have_irq; - } - } - - /* Can we find a matching entry in the interrupt-map? */ - if (pci_intmap_match(pdev, &prom_irq)) { - pdev->irq = p->irq_build(pbm, pdev, (portid << 6) | prom_irq); - goto have_irq; - } - - /* Ok, we have to do it the hard way. */ - { - unsigned int bus, slot, line; - - bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0; - - /* If we have a legal interrupt property, use it as - * the IRQ line. - */ - if (prom_irq > 0 && prom_irq < 5) { - line = ((prom_irq - 1) & 3); - } else { - u8 pci_irq_line; - - /* Else just directly consult PCI config space. */ - pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pci_irq_line); - line = ((pci_irq_line - 1) & 3); - } - - /* Now figure out the slot. - * - * Basically, device number zero on the top-level bus is - * always the PCI host controller. Slot 0 is then device 1. - * PBM A supports two external slots (0 and 1), and PBM B - * supports 4 external slots (0, 1, 2, and 3). On-board PCI - * devices are wired to device numbers outside of these - * ranges. -DaveM - */ - if (pdev->bus->number == pbm->pci_first_busno) { - slot = PCI_SLOT(pdev->devfn) - pbm->pci_first_slot; - } else { - struct pci_dev *bus_dev; - - /* Underneath a bridge, use slot number of parent - * bridge which is closest to the PBM. - */ - bus_dev = pdev->bus->self; - while (bus_dev->bus && - bus_dev->bus->number != pbm->pci_first_busno) - bus_dev = bus_dev->bus->self; - - slot = PCI_SLOT(bus_dev->devfn) - pbm->pci_first_slot; - } - slot = slot << 2; - - pdev->irq = p->irq_build(pbm, pdev, - ((portid << 6) & PCI_IRQ_IGN) | - (bus | slot | line)); - } + pdev->irq = op->irqs[0]; -have_irq: pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, pdev->irq & PCI_IRQ_INO); } diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h index 6c320596254..971e2bea30b 100644 --- a/arch/sparc64/kernel/pci_impl.h +++ b/arch/sparc64/kernel/pci_impl.h @@ -10,6 +10,7 @@ #include <linux/types.h> #include <linux/spinlock.h> #include <asm/io.h> +#include <asm/prom.h> extern struct pci_controller_info *pci_controller_root; @@ -19,7 +20,7 @@ extern int pci_num_controllers; extern void pci_fixup_host_bridge_self(struct pci_bus *pbus); extern void pci_fill_in_pbm_cookies(struct pci_bus *pbus, struct pci_pbm_info *pbm, - int prom_node); + struct device_node *prom_node); extern void pci_record_assignments(struct pci_pbm_info *pbm, struct pci_bus *pbus); extern void pci_assign_unassigned(struct pci_pbm_info *pbm, diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c index d17878b145c..197a7ffd57e 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -17,6 +17,8 @@ #include <asm/iommu.h> #include <asm/irq.h> #include <asm/starfire.h> +#include <asm/prom.h> +#include <asm/of_device.h> #include "pci_impl.h" #include "iommu_common.h" @@ -207,187 +209,6 @@ static struct pci_ops psycho_ops = { .write = psycho_write_pci_cfg, }; -/* PSYCHO interrupt mapping support. */ -#define PSYCHO_IMAP_A_SLOT0 0x0c00UL -#define PSYCHO_IMAP_B_SLOT0 0x0c20UL -static unsigned long psycho_pcislot_imap_offset(unsigned long ino) -{ - unsigned int bus = (ino & 0x10) >> 4; - unsigned int slot = (ino & 0x0c) >> 2; - - if (bus == 0) - return PSYCHO_IMAP_A_SLOT0 + (slot * 8); - else - return PSYCHO_IMAP_B_SLOT0 + (slot * 8); -} - -#define PSYCHO_IMAP_SCSI 0x1000UL -#define PSYCHO_IMAP_ETH 0x1008UL -#define PSYCHO_IMAP_BPP 0x1010UL -#define PSYCHO_IMAP_AU_REC 0x1018UL -#define PSYCHO_IMAP_AU_PLAY 0x1020UL -#define PSYCHO_IMAP_PFAIL 0x1028UL -#define PSYCHO_IMAP_KMS 0x1030UL -#define PSYCHO_IMAP_FLPY 0x1038UL -#define PSYCHO_IMAP_SHW 0x1040UL -#define PSYCHO_IMAP_KBD 0x1048UL -#define PSYCHO_IMAP_MS 0x1050UL -#define PSYCHO_IMAP_SER 0x1058UL -#define PSYCHO_IMAP_TIM0 0x1060UL -#define PSYCHO_IMAP_TIM1 0x1068UL -#define PSYCHO_IMAP_UE 0x1070UL -#define PSYCHO_IMAP_CE 0x1078UL -#define PSYCHO_IMAP_A_ERR 0x1080UL -#define PSYCHO_IMAP_B_ERR 0x1088UL -#define PSYCHO_IMAP_PMGMT 0x1090UL -#define PSYCHO_IMAP_GFX 0x1098UL -#define PSYCHO_IMAP_EUPA 0x10a0UL - -static unsigned long __onboard_imap_off[] = { -/*0x20*/ PSYCHO_IMAP_SCSI, -/*0x21*/ PSYCHO_IMAP_ETH, -/*0x22*/ PSYCHO_IMAP_BPP, -/*0x23*/ PSYCHO_IMAP_AU_REC, -/*0x24*/ PSYCHO_IMAP_AU_PLAY, -/*0x25*/ PSYCHO_IMAP_PFAIL, -/*0x26*/ PSYCHO_IMAP_KMS, -/*0x27*/ PSYCHO_IMAP_FLPY, -/*0x28*/ PSYCHO_IMAP_SHW, -/*0x29*/ PSYCHO_IMAP_KBD, -/*0x2a*/ PSYCHO_IMAP_MS, -/*0x2b*/ PSYCHO_IMAP_SER, -/*0x2c*/ PSYCHO_IMAP_TIM0, -/*0x2d*/ PSYCHO_IMAP_TIM1, -/*0x2e*/ PSYCHO_IMAP_UE, -/*0x2f*/ PSYCHO_IMAP_CE, -/*0x30*/ PSYCHO_IMAP_A_ERR, -/*0x31*/ PSYCHO_IMAP_B_ERR, -/*0x32*/ PSYCHO_IMAP_PMGMT -}; -#define PSYCHO_ONBOARD_IRQ_BASE 0x20 -#define PSYCHO_ONBOARD_IRQ_LAST 0x32 -#define psycho_onboard_imap_offset(__ino) \ - __onboard_imap_off[(__ino) - PSYCHO_ONBOARD_IRQ_BASE] - -#define PSYCHO_ICLR_A_SLOT0 0x1400UL -#define PSYCHO_ICLR_SCSI 0x1800UL - -#define psycho_iclr_offset(ino) \ - ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \ - (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3))) - -/* PCI PSYCHO INO number to Sparc PIL level. */ -static unsigned char psycho_pil_table[] = { -/*0x00*/0, 0, 0, 0, /* PCI A slot 0 Int A, B, C, D */ -/*0x04*/0, 0, 0, 0, /* PCI A slot 1 Int A, B, C, D */ -/*0x08*/0, 0, 0, 0, /* PCI A slot 2 Int A, B, C, D */ -/*0x0c*/0, 0, 0, 0, /* PCI A slot 3 Int A, B, C, D */ -/*0x10*/0, 0, 0, 0, /* PCI B slot 0 Int A, B, C, D */ -/*0x14*/0, 0, 0, 0, /* PCI B slot 1 Int A, B, C, D */ -/*0x18*/0, 0, 0, 0, /* PCI B slot 2 Int A, B, C, D */ -/*0x1c*/0, 0, 0, 0, /* PCI B slot 3 Int A, B, C, D */ -/*0x20*/5, /* SCSI */ -/*0x21*/5, /* Ethernet */ -/*0x22*/8, /* Parallel Port */ -/*0x23*/13, /* Audio Record */ -/*0x24*/14, /* Audio Playback */ -/*0x25*/15, /* PowerFail */ -/*0x26*/5, /* second SCSI */ -/*0x27*/11, /* Floppy */ -/*0x28*/5, /* Spare Hardware */ -/*0x29*/9, /* Keyboard */ -/*0x2a*/5, /* Mouse */ -/*0x2b*/12, /* Serial */ -/*0x2c*/10, /* Timer 0 */ -/*0x2d*/11, /* Timer 1 */ -/*0x2e*/15, /* Uncorrectable ECC */ -/*0x2f*/15, /* Correctable ECC */ -/*0x30*/15, /* PCI Bus A Error */ -/*0x31*/15, /* PCI Bus B Error */ -/*0x32*/15, /* Power Management */ -}; - -static int psycho_ino_to_pil(struct pci_dev *pdev, unsigned int ino) -{ - int ret; - - ret = psycho_pil_table[ino]; - if (ret == 0 && pdev == NULL) { - ret = 5; - } else if (ret == 0) { - switch ((pdev->class >> 16) & 0xff) { - case PCI_BASE_CLASS_STORAGE: - ret = 5; - break; - - case PCI_BASE_CLASS_NETWORK: - ret = 6; - break; - - case PCI_BASE_CLASS_DISPLAY: - ret = 9; - break; - - case PCI_BASE_CLASS_MULTIMEDIA: - case PCI_BASE_CLASS_MEMORY: - case PCI_BASE_CLASS_BRIDGE: - case PCI_BASE_CLASS_SERIAL: - ret = 10; - break; - - default: - ret = 5; - break; - }; - } - - return ret; -} - -static unsigned int psycho_irq_build(struct pci_pbm_info *pbm, - struct pci_dev *pdev, - unsigned int ino) -{ - struct ino_bucket *bucket; - unsigned long imap, iclr; - unsigned long imap_off, iclr_off; - int pil, inofixup = 0; - - ino &= PCI_IRQ_INO; - if (ino < PSYCHO_ONBOARD_IRQ_BASE) { - /* PCI slot */ - imap_off = psycho_pcislot_imap_offset(ino); - } else { - /* Onboard device */ - if (ino > PSYCHO_ONBOARD_IRQ_LAST) { - prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino); - prom_halt(); - } - imap_off = psycho_onboard_imap_offset(ino); - } - - /* Now build the IRQ bucket. */ - pil = psycho_ino_to_pil(pdev, ino); - - if (PIL_RESERVED(pil)) - BUG(); - - imap = pbm->controller_regs + imap_off; - imap += 4; - - iclr_off = psycho_iclr_offset(ino); - iclr = pbm->controller_regs + iclr_off; - iclr += 4; - - if ((ino & 0x20) == 0) - inofixup = ino & 0x03; - - bucket = __bucket(build_irq(pil, inofixup, iclr, imap)); - bucket->flags |= IBF_PCI; - - return __irq(bucket); -} - /* PSYCHO error handling support. */ enum psycho_error_type { UE_ERR, CE_ERR, PCI_ERR @@ -1020,51 +841,34 @@ static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id, struct pt_regs *reg #define PSYCHO_ECCCTRL_EE 0x8000000000000000UL /* Enable ECC Checking */ #define PSYCHO_ECCCTRL_UE 0x4000000000000000UL /* Enable UE Interrupts */ #define PSYCHO_ECCCTRL_CE 0x2000000000000000UL /* Enable CE INterrupts */ -#define PSYCHO_UE_INO 0x2e -#define PSYCHO_CE_INO 0x2f -#define PSYCHO_PCIERR_A_INO 0x30 -#define PSYCHO_PCIERR_B_INO 0x31 static void psycho_register_error_handlers(struct pci_controller_info *p) { struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */ + struct of_device *op = of_find_device_by_node(pbm->prom_node); unsigned long base = p->pbm_A.controller_regs; - unsigned int irq, portid = pbm->portid; u64 tmp; - /* Build IRQs and register handlers. */ - irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_UE_INO); - if (request_irq(irq, psycho_ue_intr, - SA_SHIRQ, "PSYCHO UE", p) < 0) { - prom_printf("PSYCHO%d: Cannot register UE interrupt.\n", - p->index); - prom_halt(); - } + if (!op) + return; - irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_CE_INO); - if (request_irq(irq, psycho_ce_intr, - SA_SHIRQ, "PSYCHO CE", p) < 0) { - prom_printf("PSYCHO%d: Cannot register CE interrupt.\n", - p->index); - prom_halt(); - } + /* Psycho interrupt property order is: + * 0: PCIERR PBM B INO + * 1: UE ERR + * 2: CE ERR + * 3: POWER FAIL + * 4: SPARE HARDWARE + * 5: PCIERR PBM A INO + */ - pbm = &p->pbm_A; - irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_A_INO); - if (request_irq(irq, psycho_pcierr_intr, - SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_A) < 0) { - prom_printf("PSYCHO%d(PBMA): Cannot register PciERR interrupt.\n", - p->index); - prom_halt(); - } + if (op->num_irqs < 6) + return; - pbm = &p->pbm_B; - irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_B_INO); - if (request_irq(irq, psycho_pcierr_intr, - SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_B) < 0) { - prom_printf("PSYCHO%d(PBMB): Cannot register PciERR interrupt.\n", - p->index); - prom_halt(); - } + request_irq(op->irqs[1], psycho_ue_intr, IRQF_SHARED, "PSYCHO UE", p); + request_irq(op->irqs[2], psycho_ce_intr, IRQF_SHARED, "PSYCHO CE", p); + request_irq(op->irqs[5], psycho_pcierr_intr, IRQF_SHARED, + "PSYCHO PCIERR-A", &p->pbm_A); + request_irq(op->irqs[0], psycho_pcierr_intr, IRQF_SHARED, + "PSYCHO PCIERR-B", &p->pbm_B); /* Enable UE and CE interrupts for controller. */ psycho_write(base + PSYCHO_ECC_CTRL, @@ -1247,9 +1051,7 @@ static void psycho_iommu_init(struct pci_controller_info *p) /* If necessary, hook us up for starfire IRQ translations. */ if (this_is_starfire) - p->starfire_cookie = starfire_hookup(p->pbm_A.portid); - else - p->starfire_cookie = NULL; + starfire_hookup(p->pbm_A.portid); } #define PSYCHO_IRQ_RETRY 0x1a00UL @@ -1368,11 +1170,12 @@ static void psycho_pbm_strbuf_init(struct pci_controller_info *p, #define PSYCHO_MEMSPACE_SIZE 0x07fffffffUL static void psycho_pbm_init(struct pci_controller_info *p, - int prom_node, int is_pbm_a) + struct device_node *dp, int is_pbm_a) { - unsigned int busrange[2]; + unsigned int *busrange; + struct property *prop; struct pci_pbm_info *pbm; - int err; + int len; if (is_pbm_a) { pbm = &p->pbm_A; @@ -1387,10 +1190,14 @@ static void psycho_pbm_init(struct pci_controller_info *p, } pbm->chip_type = PBM_CHIP_TYPE_PSYCHO; - pbm->chip_version = - prom_getintdefault(prom_node, "version#", 0); - pbm->chip_revision = - prom_getintdefault(prom_node, "module-revision#", 0); + pbm->chip_version = 0; + prop = of_find_property(dp, "version#", NULL); + if (prop) + pbm->chip_version = *(int *) prop->value; + pbm->chip_revision = 0; + prop = of_find_property(dp, "module-revision#", NULL); + if (prop) + pbm->chip_revision = *(int *) prop->value; pbm->io_space.end = pbm->io_space.start + PSYCHO_IOSPACE_SIZE; pbm->io_space.flags = IORESOURCE_IO; @@ -1399,45 +1206,36 @@ static void psycho_pbm_init(struct pci_controller_info *p, pbm_register_toplevel_resources(p, pbm); pbm->parent = p; - pbm->prom_node = prom_node; - prom_getstring(prom_node, "name", - pbm->prom_name, - sizeof(pbm->prom_name)); - - err = prom_getproperty(prom_node, "ranges", - (char *)pbm->pbm_ranges, - sizeof(pbm->pbm_ranges)); - if (err != -1) + pbm->prom_node = dp; + pbm->name = dp->full_name; + + printk("%s: PSYCHO PCI Bus Module ver[%x:%x]\n", + pbm->name, + pbm->chip_version, pbm->chip_revision); + + prop = of_find_property(dp, "ranges", &len); + if (prop) { + pbm->pbm_ranges = prop->value; pbm->num_pbm_ranges = - (err / sizeof(struct linux_prom_pci_ranges)); - else + (len / sizeof(struct linux_prom_pci_ranges)); + } else { pbm->num_pbm_ranges = 0; + } - err = prom_getproperty(prom_node, "interrupt-map", - (char *)pbm->pbm_intmap, - sizeof(pbm->pbm_intmap)); - if (err != -1) { - pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); - err = prom_getproperty(prom_node, "interrupt-map-mask", - (char *)&pbm->pbm_intmask, - sizeof(pbm->pbm_intmask)); - if (err == -1) { - prom_printf("PSYCHO-PBM: Fatal error, no " - "interrupt-map-mask.\n"); - prom_halt(); - } + prop = of_find_property(dp, "interrupt-map", &len); + if (prop) { + pbm->pbm_intmap = prop->value; + pbm->num_pbm_intmap = + (len / sizeof(struct linux_prom_pci_intmap)); + + prop = of_find_property(dp, "interrupt-map-mask", NULL); + pbm->pbm_intmask = prop->value; } else { pbm->num_pbm_intmap = 0; - memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); } - err = prom_getproperty(prom_node, "bus-range", - (char *)&busrange[0], - sizeof(busrange)); - if (err == 0 || err == -1) { - prom_printf("PSYCHO-PBM: Fatal error, no bus-range.\n"); - prom_halt(); - } + prop = of_find_property(dp, "bus-range", NULL); + busrange = prop->value; pbm->pci_first_busno = busrange[0]; pbm->pci_last_busno = busrange[1]; @@ -1446,20 +1244,24 @@ static void psycho_pbm_init(struct pci_controller_info *p, #define PSYCHO_CONFIGSPACE 0x001000000UL -void psycho_init(int node, char *model_name) +void psycho_init(struct device_node *dp, char *model_name) { - struct linux_prom64_registers pr_regs[3]; + struct linux_prom64_registers *pr_regs; struct pci_controller_info *p; struct pci_iommu *iommu; + struct property *prop; u32 upa_portid; - int is_pbm_a, err; + int is_pbm_a; - upa_portid = prom_getintdefault(node, "upa-portid", 0xff); + upa_portid = 0xff; + prop = of_find_property(dp, "upa-portid", NULL); + if (prop) + upa_portid = *(u32 *) prop->value; for(p = pci_controller_root; p; p = p->next) { if (p->pbm_A.portid == upa_portid) { - is_pbm_a = (p->pbm_A.prom_node == 0); - psycho_pbm_init(p, node, is_pbm_a); + is_pbm_a = (p->pbm_A.prom_node == NULL); + psycho_pbm_init(p, dp, is_pbm_a); return; } } @@ -1484,28 +1286,18 @@ void psycho_init(int node, char *model_name) p->index = pci_num_controllers++; p->pbms_same_domain = 0; p->scan_bus = psycho_scan_bus; - p->irq_build = psycho_irq_build; p->base_address_update = psycho_base_address_update; p->resource_adjust = psycho_resource_adjust; p->pci_ops = &psycho_ops; - err = prom_getproperty(node, "reg", - (char *)&pr_regs[0], - sizeof(pr_regs)); - if (err == 0 || err == -1) { - prom_printf("PSYCHO: Fatal error, no reg property.\n"); - prom_halt(); - } + prop = of_find_property(dp, "reg", NULL); + pr_regs = prop->value; p->pbm_A.controller_regs = pr_regs[2].phys_addr; p->pbm_B.controller_regs = pr_regs[2].phys_addr; - printk("PCI: Found PSYCHO, control regs at %016lx\n", - p->pbm_A.controller_regs); p->pbm_A.config_space = p->pbm_B.config_space = (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE); - printk("PSYCHO: Shared PCI config space at %016lx\n", - p->pbm_A.config_space); /* * Psycho's PCI MEM space is mapped to a 2GB aligned area, so @@ -1518,5 +1310,5 @@ void psycho_init(int node, char *model_name) psycho_iommu_init(p); is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); - psycho_pbm_init(p, node, is_pbm_a); + psycho_pbm_init(p, dp, is_pbm_a); } diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index f67bb7f078c..45891850b90 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -19,6 +19,7 @@ #include <asm/irq.h> #include <asm/smp.h> #include <asm/oplib.h> +#include <asm/prom.h> #include "pci_impl.h" #include "iommu_common.h" @@ -484,191 +485,6 @@ static struct pci_ops sabre_ops = { .write = sabre_write_pci_cfg, }; -static unsigned long sabre_pcislot_imap_offset(unsigned long ino) -{ - unsigned int bus = (ino & 0x10) >> 4; - unsigned int slot = (ino & 0x0c) >> 2; - - if (bus == 0) - return SABRE_IMAP_A_SLOT0 + (slot * 8); - else - return SABRE_IMAP_B_SLOT0 + (slot * 8); -} - -static unsigned long __onboard_imap_off[] = { -/*0x20*/ SABRE_IMAP_SCSI, -/*0x21*/ SABRE_IMAP_ETH, -/*0x22*/ SABRE_IMAP_BPP, -/*0x23*/ SABRE_IMAP_AU_REC, -/*0x24*/ SABRE_IMAP_AU_PLAY, -/*0x25*/ SABRE_IMAP_PFAIL, -/*0x26*/ SABRE_IMAP_KMS, -/*0x27*/ SABRE_IMAP_FLPY, -/*0x28*/ SABRE_IMAP_SHW, -/*0x29*/ SABRE_IMAP_KBD, -/*0x2a*/ SABRE_IMAP_MS, -/*0x2b*/ SABRE_IMAP_SER, -/*0x2c*/ 0 /* reserved */, -/*0x2d*/ 0 /* reserved */, -/*0x2e*/ SABRE_IMAP_UE, -/*0x2f*/ SABRE_IMAP_CE, -/*0x30*/ SABRE_IMAP_PCIERR, -}; -#define SABRE_ONBOARD_IRQ_BASE 0x20 -#define SABRE_ONBOARD_IRQ_LAST 0x30 -#define sabre_onboard_imap_offset(__ino) \ - __onboard_imap_off[(__ino) - SABRE_ONBOARD_IRQ_BASE] - -#define sabre_iclr_offset(ino) \ - ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \ - (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3))) - -/* PCI SABRE INO number to Sparc PIL level. */ -static unsigned char sabre_pil_table[] = { -/*0x00*/0, 0, 0, 0, /* PCI A slot 0 Int A, B, C, D */ -/*0x04*/0, 0, 0, 0, /* PCI A slot 1 Int A, B, C, D */ -/*0x08*/0, 0, 0, 0, /* PCI A slot 2 Int A, B, C, D */ -/*0x0c*/0, 0, 0, 0, /* PCI A slot 3 Int A, B, C, D */ -/*0x10*/0, 0, 0, 0, /* PCI B slot 0 Int A, B, C, D */ -/*0x14*/0, 0, 0, 0, /* PCI B slot 1 Int A, B, C, D */ -/*0x18*/0, 0, 0, 0, /* PCI B slot 2 Int A, B, C, D */ -/*0x1c*/0, 0, 0, 0, /* PCI B slot 3 Int A, B, C, D */ -/*0x20*/5, /* SCSI */ -/*0x21*/5, /* Ethernet */ -/*0x22*/8, /* Parallel Port */ -/*0x23*/13, /* Audio Record */ -/*0x24*/14, /* Audio Playback */ -/*0x25*/15, /* PowerFail */ -/*0x26*/5, /* second SCSI */ -/*0x27*/11, /* Floppy */ -/*0x28*/5, /* Spare Hardware */ -/*0x29*/9, /* Keyboard */ -/*0x2a*/5, /* Mouse */ -/*0x2b*/12, /* Serial */ -/*0x2c*/10, /* Timer 0 */ -/*0x2d*/11, /* Timer 1 */ -/*0x2e*/15, /* Uncorrectable ECC */ -/*0x2f*/15, /* Correctable ECC */ -/*0x30*/15, /* PCI Bus A Error */ -/*0x31*/15, /* PCI Bus B Error */ -/*0x32*/15, /* Power Management */ -}; - -static int sabre_ino_to_pil(struct pci_dev *pdev, unsigned int ino) -{ - int ret; - - if (pdev && - pdev->vendor == PCI_VENDOR_ID_SUN && - pdev->device == PCI_DEVICE_ID_SUN_RIO_USB) - return 9; - - ret = sabre_pil_table[ino]; - if (ret == 0 && pdev == NULL) { - ret = 5; - } else if (ret == 0) { - switch ((pdev->class >> 16) & 0xff) { - case PCI_BASE_CLASS_STORAGE: - ret = 5; - break; - - case PCI_BASE_CLASS_NETWORK: - ret = 6; - break; - - case PCI_BASE_CLASS_DISPLAY: - ret = 9; - break; - - case PCI_BASE_CLASS_MULTIMEDIA: - case PCI_BASE_CLASS_MEMORY: - case PCI_BASE_CLASS_BRIDGE: - case PCI_BASE_CLASS_SERIAL: - ret = 10; - break; - - default: - ret = 5; - break; - }; - } - return ret; -} - -/* When a device lives behind a bridge deeper in the PCI bus topology - * than APB, a special sequence must run to make sure all pending DMA - * transfers at the time of IRQ delivery are visible in the coherency - * domain by the cpu. This sequence is to perform a read on the far - * side of the non-APB bridge, then perform a read of Sabre's DMA - * write-sync register. - */ -static void sabre_wsync_handler(struct ino_bucket *bucket, void *_arg1, void *_arg2) -{ - struct pci_dev *pdev = _arg1; - unsigned long sync_reg = (unsigned long) _arg2; - u16 _unused; - - pci_read_config_word(pdev, PCI_VENDOR_ID, &_unused); - sabre_read(sync_reg); -} - -static unsigned int sabre_irq_build(struct pci_pbm_info *pbm, - struct pci_dev *pdev, - unsigned int ino) -{ - struct ino_bucket *bucket; - unsigned long imap, iclr; - unsigned long imap_off, iclr_off; - int pil, inofixup = 0; - - ino &= PCI_IRQ_INO; - if (ino < SABRE_ONBOARD_IRQ_BASE) { - /* PCI slot */ - imap_off = sabre_pcislot_imap_offset(ino); - } else { - /* onboard device */ - if (ino > SABRE_ONBOARD_IRQ_LAST) { - prom_printf("sabre_irq_build: Wacky INO [%x]\n", ino); - prom_halt(); - } - imap_off = sabre_onboard_imap_offset(ino); - } - - /* Now build the IRQ bucket. */ - pil = sabre_ino_to_pil(pdev, ino); - - if (PIL_RESERVED(pil)) - BUG(); - - imap = pbm->controller_regs + imap_off; - imap += 4; - - iclr_off = sabre_iclr_offset(ino); - iclr = pbm->controller_regs + iclr_off; - iclr += 4; - - if ((ino & 0x20) == 0) - inofixup = ino & 0x03; - - bucket = __bucket(build_irq(pil, inofixup, iclr, imap)); - bucket->flags |= IBF_PCI; - - if (pdev) { - struct pcidev_cookie *pcp = pdev->sysdata; - - if (pdev->bus->number != pcp->pbm->pci_first_busno) { - struct pci_controller_info *p = pcp->pbm->parent; - struct irq_desc *d = bucket->irq_info; - - d->pre_handler = sabre_wsync_handler; - d->pre_handler_arg1 = pdev; - d->pre_handler_arg2 = (void *) - p->pbm_A.controller_regs + SABRE_WRSYNC; - } - } - return __irq(bucket); -} - /* SABRE error handling support. */ static void sabre_check_iommu_error(struct pci_controller_info *p, unsigned long afsr, @@ -1005,17 +821,30 @@ static irqreturn_t sabre_pcierr_intr(int irq, void *dev_id, struct pt_regs *regs return IRQ_HANDLED; } -/* XXX What about PowerFail/PowerManagement??? -DaveM */ -#define SABRE_UE_INO 0x2e -#define SABRE_CE_INO 0x2f -#define SABRE_PCIERR_INO 0x30 static void sabre_register_error_handlers(struct pci_controller_info *p) { struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */ + struct device_node *dp = pbm->prom_node; + struct of_device *op; unsigned long base = pbm->controller_regs; - unsigned long irq, portid = pbm->portid; u64 tmp; + if (pbm->chip_type == PBM_CHIP_TYPE_SABRE) + dp = dp->parent; + + op = of_find_device_by_node(dp); + if (!op) + return; + + /* Sabre/Hummingbird IRQ property layout is: + * 0: PCI ERR + * 1: UE ERR + * 2: CE ERR + * 3: POWER FAIL + */ + if (op->num_irqs < 4) + return; + /* We clear the error bits in the appropriate AFSR before * registering the handler so that we don't get spurious * interrupts. @@ -1024,32 +853,16 @@ static void sabre_register_error_handlers(struct pci_controller_info *p) (SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR | SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR | SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE)); - irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_UE_INO); - if (request_irq(irq, sabre_ue_intr, - SA_SHIRQ, "SABRE UE", p) < 0) { - prom_printf("SABRE%d: Cannot register UE interrupt.\n", - p->index); - prom_halt(); - } + + request_irq(op->irqs[1], sabre_ue_intr, IRQF_SHARED, "SABRE UE", p); sabre_write(base + SABRE_CE_AFSR, (SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR | SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR)); - irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_CE_INO); - if (request_irq(irq, sabre_ce_intr, - SA_SHIRQ, "SABRE CE", p) < 0) { - prom_printf("SABRE%d: Cannot register CE interrupt.\n", - p->index); - prom_halt(); - } - irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_PCIERR_INO); - if (request_irq(irq, sabre_pcierr_intr, - SA_SHIRQ, "SABRE PCIERR", p) < 0) { - prom_printf("SABRE%d: Cannot register PciERR interrupt.\n", - p->index); - prom_halt(); - } + request_irq(op->irqs[2], sabre_ce_intr, IRQF_SHARED, "SABRE CE", p); + request_irq(op->irqs[0], sabre_pcierr_intr, IRQF_SHARED, + "SABRE PCIERR", p); tmp = sabre_read(base + SABRE_PCICTRL); tmp |= SABRE_PCICTRL_ERREN; @@ -1383,34 +1196,36 @@ static void pbm_register_toplevel_resources(struct pci_controller_info *p, &pbm->mem_space); } -static void sabre_pbm_init(struct pci_controller_info *p, int sabre_node, u32 dma_begin) +static void sabre_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 dma_begin) { struct pci_pbm_info *pbm; - char namebuf[128]; - u32 busrange[2]; - int node, simbas_found; + struct device_node *node; + struct property *prop; + u32 *busrange; + int len, simbas_found; simbas_found = 0; - node = prom_getchild(sabre_node); - while ((node = prom_searchsiblings(node, "pci")) != 0) { - int err; - - err = prom_getproperty(node, "model", namebuf, sizeof(namebuf)); - if ((err <= 0) || strncmp(namebuf, "SUNW,simba", err)) + node = dp->child; + while (node != NULL) { + if (strcmp(node->name, "pci")) goto next_pci; - err = prom_getproperty(node, "bus-range", - (char *)&busrange[0], sizeof(busrange)); - if (err == 0 || err == -1) { - prom_printf("APB: Error, cannot get PCI bus-range.\n"); - prom_halt(); - } + prop = of_find_property(node, "model", NULL); + if (!prop || strncmp(prop->value, "SUNW,simba", prop->length)) + goto next_pci; simbas_found++; + + prop = of_find_property(node, "bus-range", NULL); + busrange = prop->value; if (busrange[0] == 1) pbm = &p->pbm_B; else pbm = &p->pbm_A; + + pbm->name = node->full_name; + printk("%s: SABRE PCI Bus Module\n", pbm->name); + pbm->chip_type = PBM_CHIP_TYPE_SABRE; pbm->parent = p; pbm->prom_node = node; @@ -1418,83 +1233,68 @@ static void sabre_pbm_init(struct pci_controller_info *p, int sabre_node, u32 dm pbm->pci_first_busno = busrange[0]; pbm->pci_last_busno = busrange[1]; - prom_getstring(node, "name", pbm->prom_name, sizeof(pbm->prom_name)); - err = prom_getproperty(node, "ranges", - (char *)pbm->pbm_ranges, - sizeof(pbm->pbm_ranges)); - if (err != -1) + prop = of_find_property(node, "ranges", &len); + if (prop) { + pbm->pbm_ranges = prop->value; pbm->num_pbm_ranges = - (err / sizeof(struct linux_prom_pci_ranges)); - else + (len / sizeof(struct linux_prom_pci_ranges)); + } else { pbm->num_pbm_ranges = 0; + } - err = prom_getproperty(node, "interrupt-map", - (char *)pbm->pbm_intmap, - sizeof(pbm->pbm_intmap)); - if (err != -1) { - pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); - err = prom_getproperty(node, "interrupt-map-mask", - (char *)&pbm->pbm_intmask, - sizeof(pbm->pbm_intmask)); - if (err == -1) { - prom_printf("APB: Fatal error, no interrupt-map-mask.\n"); - prom_halt(); - } + prop = of_find_property(node, "interrupt-map", &len); + if (prop) { + pbm->pbm_intmap = prop->value; + pbm->num_pbm_intmap = + (len / sizeof(struct linux_prom_pci_intmap)); + + prop = of_find_property(node, "interrupt-map-mask", + NULL); + pbm->pbm_intmask = prop->value; } else { pbm->num_pbm_intmap = 0; - memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); } pbm_register_toplevel_resources(p, pbm); next_pci: - node = prom_getsibling(node); - if (!node) - break; + node = node->sibling; } if (simbas_found == 0) { - int err; - /* No APBs underneath, probably this is a hummingbird * system. */ pbm = &p->pbm_A; pbm->parent = p; - pbm->prom_node = sabre_node; + pbm->prom_node = dp; pbm->pci_first_busno = p->pci_first_busno; pbm->pci_last_busno = p->pci_last_busno; - prom_getstring(sabre_node, "name", pbm->prom_name, sizeof(pbm->prom_name)); - err = prom_getproperty(sabre_node, "ranges", - (char *) pbm->pbm_ranges, - sizeof(pbm->pbm_ranges)); - if (err != -1) + prop = of_find_property(dp, "ranges", &len); + if (prop) { + pbm->pbm_ranges = prop->value; pbm->num_pbm_ranges = - (err / sizeof(struct linux_prom_pci_ranges)); - else + (len / sizeof(struct linux_prom_pci_ranges)); + } else { pbm->num_pbm_ranges = 0; + } - err = prom_getproperty(sabre_node, "interrupt-map", - (char *) pbm->pbm_intmap, - sizeof(pbm->pbm_intmap)); - - if (err != -1) { - pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); - err = prom_getproperty(sabre_node, "interrupt-map-mask", - (char *)&pbm->pbm_intmask, - sizeof(pbm->pbm_intmask)); - if (err == -1) { - prom_printf("Hummingbird: Fatal error, no interrupt-map-mask.\n"); - prom_halt(); - } + prop = of_find_property(dp, "interrupt-map", &len); + if (prop) { + pbm->pbm_intmap = prop->value; + pbm->num_pbm_intmap = + (len / sizeof(struct linux_prom_pci_intmap)); + + prop = of_find_property(dp, "interrupt-map-mask", + NULL); + pbm->pbm_intmask = prop->value; } else { pbm->num_pbm_intmap = 0; - memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); } + pbm->name = dp->full_name; + printk("%s: SABRE PCI Bus Module\n", pbm->name); - sprintf(pbm->name, "SABRE%d PBM%c", p->index, - (pbm == &p->pbm_A ? 'A' : 'B')); pbm->io_space.name = pbm->mem_space.name = pbm->name; /* Hack up top-level resources. */ @@ -1520,14 +1320,15 @@ static void sabre_pbm_init(struct pci_controller_info *p, int sabre_node, u32 dm } } -void sabre_init(int pnode, char *model_name) +void sabre_init(struct device_node *dp, char *model_name) { - struct linux_prom64_registers pr_regs[2]; + struct linux_prom64_registers *pr_regs; struct pci_controller_info *p; struct pci_iommu *iommu; - int tsbsize, err; - u32 busrange[2]; - u32 vdma[2]; + struct property *prop; + int tsbsize; + u32 *busrange; + u32 *vdma; u32 upa_portid, dma_mask; u64 clear_irq; @@ -1535,22 +1336,21 @@ void sabre_init(int pnode, char *model_name) if (!strcmp(model_name, "pci108e,a001")) hummingbird_p = 1; else if (!strcmp(model_name, "SUNW,sabre")) { - char compat[64]; + prop = of_find_property(dp, "compatible", NULL); + if (prop) { + const char *compat = prop->value; - if (prom_getproperty(pnode, "compatible", - compat, sizeof(compat)) > 0 && - !strcmp(compat, "pci108e,a001")) { - hummingbird_p = 1; - } else { - int cpu_node; + if (!strcmp(compat, "pci108e,a001")) + hummingbird_p = 1; + } + if (!hummingbird_p) { + struct device_node *dp; /* Of course, Sun has to encode things a thousand * different ways, inconsistently. */ - cpu_find_by_instance(0, &cpu_node, NULL); - if (prom_getproperty(cpu_node, "name", - compat, sizeof(compat)) > 0 && - !strcmp(compat, "SUNW,UltraSPARC-IIe")) + cpu_find_by_instance(0, &dp, NULL); + if (!strcmp(dp->name, "SUNW,UltraSPARC-IIe")) hummingbird_p = 1; } } @@ -1568,7 +1368,10 @@ void sabre_init(int pnode, char *model_name) } p->pbm_A.iommu = p->pbm_B.iommu = iommu; - upa_portid = prom_getintdefault(pnode, "upa-portid", 0xff); + upa_portid = 0xff; + prop = of_find_property(dp, "upa-portid", NULL); + if (prop) + upa_portid = *(u32 *) prop->value; p->next = pci_controller_root; pci_controller_root = p; @@ -1578,7 +1381,6 @@ void sabre_init(int pnode, char *model_name) p->index = pci_num_controllers++; p->pbms_same_domain = 1; p->scan_bus = sabre_scan_bus; - p->irq_build = sabre_irq_build; p->base_address_update = sabre_base_address_update; p->resource_adjust = sabre_resource_adjust; p->pci_ops = &sabre_ops; @@ -1586,13 +1388,9 @@ void sabre_init(int pnode, char *model_name) /* * Map in SABRE register set and report the presence of this SABRE. */ - err = prom_getproperty(pnode, "reg", - (char *)&pr_regs[0], sizeof(pr_regs)); - if(err == 0 || err == -1) { - prom_printf("SABRE: Error, cannot get U2P registers " - "from PROM.\n"); - prom_halt(); - } + + prop = of_find_property(dp, "reg", NULL); + pr_regs = prop->value; /* * First REG in property is base of entire SABRE register space. @@ -1600,9 +1398,6 @@ void sabre_init(int pnode, char *model_name) p->pbm_A.controller_regs = pr_regs[0].phys_addr; p->pbm_B.controller_regs = pr_regs[0].phys_addr; - printk("PCI: Found SABRE, main regs at %016lx\n", - p->pbm_A.controller_regs); - /* Clear interrupts */ /* PCI first */ @@ -1621,16 +1416,9 @@ void sabre_init(int pnode, char *model_name) /* Now map in PCI config space for entire SABRE. */ p->pbm_A.config_space = p->pbm_B.config_space = (p->pbm_A.controller_regs + SABRE_CONFIGSPACE); - printk("SABRE: Shared PCI config space at %016lx\n", - p->pbm_A.config_space); - - err = prom_getproperty(pnode, "virtual-dma", - (char *)&vdma[0], sizeof(vdma)); - if(err == 0 || err == -1) { - prom_printf("SABRE: Error, cannot get virtual-dma property " - "from PROM.\n"); - prom_halt(); - } + + prop = of_find_property(dp, "virtual-dma", NULL); + vdma = prop->value; dma_mask = vdma[0]; switch(vdma[1]) { @@ -1654,21 +1442,13 @@ void sabre_init(int pnode, char *model_name) sabre_iommu_init(p, tsbsize, vdma[0], dma_mask); - printk("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]); - - err = prom_getproperty(pnode, "bus-range", - (char *)&busrange[0], sizeof(busrange)); - if(err == 0 || err == -1) { - prom_printf("SABRE: Error, cannot get PCI bus-range " - " from PROM.\n"); - prom_halt(); - } - + prop = of_find_property(dp, "bus-range", NULL); + busrange = prop->value; p->pci_first_busno = busrange[0]; p->pci_last_busno = busrange[1]; /* * Look for APB underneath. */ - sabre_pbm_init(p, pnode, vdma[0]); + sabre_pbm_init(p, dp, vdma[0]); } diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c index 7fe4de03ac2..75ade83ecc6 100644 --- a/arch/sparc64/kernel/pci_schizo.c +++ b/arch/sparc64/kernel/pci_schizo.c @@ -16,6 +16,7 @@ #include <asm/irq.h> #include <asm/upa.h> #include <asm/pstate.h> +#include <asm/prom.h> #include "pci_impl.h" #include "iommu_common.h" @@ -216,210 +217,6 @@ static struct pci_ops schizo_ops = { .write = schizo_write_pci_cfg, }; -/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the - * imap/iclr registers are per-PBM. - */ -#define SCHIZO_IMAP_BASE 0x1000UL -#define SCHIZO_ICLR_BASE 0x1400UL - -static unsigned long schizo_imap_offset(unsigned long ino) -{ - return SCHIZO_IMAP_BASE + (ino * 8UL); -} - -static unsigned long schizo_iclr_offset(unsigned long ino) -{ - return SCHIZO_ICLR_BASE + (ino * 8UL); -} - -/* PCI SCHIZO INO number to Sparc PIL level. This table only matters for - * INOs which will not have an associated PCI device struct, ie. onboard - * EBUS devices and PCI controller internal error interrupts. - */ -static unsigned char schizo_pil_table[] = { -/*0x00*/0, 0, 0, 0, /* PCI slot 0 Int A, B, C, D */ -/*0x04*/0, 0, 0, 0, /* PCI slot 1 Int A, B, C, D */ -/*0x08*/0, 0, 0, 0, /* PCI slot 2 Int A, B, C, D */ -/*0x0c*/0, 0, 0, 0, /* PCI slot 3 Int A, B, C, D */ -/*0x10*/0, 0, 0, 0, /* PCI slot 4 Int A, B, C, D */ -/*0x14*/0, 0, 0, 0, /* PCI slot 5 Int A, B, C, D */ -/*0x18*/5, /* SCSI */ -/*0x19*/5, /* second SCSI */ -/*0x1a*/0, /* UNKNOWN */ -/*0x1b*/0, /* UNKNOWN */ -/*0x1c*/8, /* Parallel */ -/*0x1d*/5, /* Ethernet */ -/*0x1e*/8, /* Firewire-1394 */ -/*0x1f*/9, /* USB */ -/*0x20*/13, /* Audio Record */ -/*0x21*/14, /* Audio Playback */ -/*0x22*/12, /* Serial */ -/*0x23*/5, /* EBUS I2C */ -/*0x24*/10, /* RTC Clock */ -/*0x25*/11, /* Floppy */ -/*0x26*/0, /* UNKNOWN */ -/*0x27*/0, /* UNKNOWN */ -/*0x28*/0, /* UNKNOWN */ -/*0x29*/0, /* UNKNOWN */ -/*0x2a*/10, /* UPA 1 */ -/*0x2b*/10, /* UPA 2 */ -/*0x2c*/0, /* UNKNOWN */ -/*0x2d*/0, /* UNKNOWN */ -/*0x2e*/0, /* UNKNOWN */ -/*0x2f*/0, /* UNKNOWN */ -/*0x30*/15, /* Uncorrectable ECC */ -/*0x31*/15, /* Correctable ECC */ -/*0x32*/15, /* PCI Bus A Error */ -/*0x33*/15, /* PCI Bus B Error */ -/*0x34*/15, /* Safari Bus Error */ -/*0x35*/0, /* Reserved */ -/*0x36*/0, /* Reserved */ -/*0x37*/0, /* Reserved */ -/*0x38*/0, /* Reserved for NewLink */ -/*0x39*/0, /* Reserved for NewLink */ -/*0x3a*/0, /* Reserved for NewLink */ -/*0x3b*/0, /* Reserved for NewLink */ -/*0x3c*/0, /* Reserved for NewLink */ -/*0x3d*/0, /* Reserved for NewLink */ -/*0x3e*/0, /* Reserved for NewLink */ -/*0x3f*/0, /* Reserved for NewLink */ -}; - -static int schizo_ino_to_pil(struct pci_dev *pdev, unsigned int ino) -{ - int ret; - - if (pdev && - pdev->vendor == PCI_VENDOR_ID_SUN && - pdev->device == PCI_DEVICE_ID_SUN_RIO_USB) - return 9; - - ret = schizo_pil_table[ino]; - if (ret == 0 && pdev == NULL) { - ret = 5; - } else if (ret == 0) { - switch ((pdev->class >> 16) & 0xff) { - case PCI_BASE_CLASS_STORAGE: - ret = 5; - break; - - case PCI_BASE_CLASS_NETWORK: - ret = 6; - break; - - case PCI_BASE_CLASS_DISPLAY: - ret = 9; - break; - - case PCI_BASE_CLASS_MULTIMEDIA: - case PCI_BASE_CLASS_MEMORY: - case PCI_BASE_CLASS_BRIDGE: - case PCI_BASE_CLASS_SERIAL: - ret = 10; - break; - - default: - ret = 5; - break; - }; - } - - return ret; -} - -static void tomatillo_wsync_handler(struct ino_bucket *bucket, void *_arg1, void *_arg2) -{ - unsigned long sync_reg = (unsigned long) _arg2; - u64 mask = 1UL << (__irq_ino(__irq(bucket)) & IMAP_INO); - u64 val; - int limit; - - schizo_write(sync_reg, mask); - - limit = 100000; - val = 0; - while (--limit) { - val = schizo_read(sync_reg); - if (!(val & mask)) - break; - } - if (limit <= 0) { - printk("tomatillo_wsync_handler: DMA won't sync [%lx:%lx]\n", - val, mask); - } - - if (_arg1) { - static unsigned char cacheline[64] - __attribute__ ((aligned (64))); - - __asm__ __volatile__("rd %%fprs, %0\n\t" - "or %0, %4, %1\n\t" - "wr %1, 0x0, %%fprs\n\t" - "stda %%f0, [%5] %6\n\t" - "wr %0, 0x0, %%fprs\n\t" - "membar #Sync" - : "=&r" (mask), "=&r" (val) - : "0" (mask), "1" (val), - "i" (FPRS_FEF), "r" (&cacheline[0]), - "i" (ASI_BLK_COMMIT_P)); - } -} - -static unsigned int schizo_irq_build(struct pci_pbm_info *pbm, - struct pci_dev *pdev, - unsigned int ino) -{ - struct ino_bucket *bucket; - unsigned long imap, iclr; - unsigned long imap_off, iclr_off; - int pil, ign_fixup; - - ino &= PCI_IRQ_INO; - imap_off = schizo_imap_offset(ino); - - /* Now build the IRQ bucket. */ - pil = schizo_ino_to_pil(pdev, ino); - - if (PIL_RESERVED(pil)) - BUG(); - - imap = pbm->pbm_regs + imap_off; - imap += 4; - - iclr_off = schizo_iclr_offset(ino); - iclr = pbm->pbm_regs + iclr_off; - iclr += 4; - - /* On Schizo, no inofixup occurs. This is because each - * INO has it's own IMAP register. On Psycho and Sabre - * there is only one IMAP register for each PCI slot even - * though four different INOs can be generated by each - * PCI slot. - * - * But, for JBUS variants (essentially, Tomatillo), we have - * to fixup the lowest bit of the interrupt group number. - */ - ign_fixup = 0; - if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) { - if (pbm->portid & 1) - ign_fixup = (1 << 6); - } - - bucket = __bucket(build_irq(pil, ign_fixup, iclr, imap)); - bucket->flags |= IBF_PCI; - - if (pdev && pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) { - struct irq_desc *p = bucket->irq_info; - - p->pre_handler = tomatillo_wsync_handler; - p->pre_handler_arg1 = ((pbm->chip_version <= 4) ? - (void *) 1 : (void *) 0); - p->pre_handler_arg2 = (void *) pbm->sync_reg; - } - - return __irq(bucket); -} - /* SCHIZO error handling support. */ enum schizo_error_type { UE_ERR, CE_ERR, PCI_ERR, SAFARI_ERR @@ -455,35 +252,6 @@ struct pci_pbm_info *pbm_for_ino(struct pci_controller_info *p, u32 ino) return &p->pbm_A; } -static void schizo_clear_other_err_intr(struct pci_controller_info *p, int irq) -{ - struct pci_pbm_info *pbm; - struct ino_bucket *bucket; - unsigned long iclr; - - /* Do not clear the interrupt for the other PCI bus. - * - * This "ACK both PBM IRQs" only needs to be performed - * for chip-wide error interrupts. - */ - if ((irq & IMAP_INO) == SCHIZO_PCIERR_A_INO || - (irq & IMAP_INO) == SCHIZO_PCIERR_B_INO) - return; - - pbm = pbm_for_ino(p, irq); - if (pbm == &p->pbm_A) - pbm = &p->pbm_B; - else - pbm = &p->pbm_A; - - irq = schizo_irq_build(pbm, NULL, - (pbm->portid << 6) | (irq & IMAP_INO)); - bucket = __bucket(irq); - iclr = bucket->iclr; - - upa_writel(ICLR_IDLE, iclr); -} - #define SCHIZO_STC_ERR 0xb800UL /* --> 0xba00 */ #define SCHIZO_STC_TAG 0xba00UL /* --> 0xba80 */ #define SCHIZO_STC_LINE 0xbb00UL /* --> 0xbb80 */ @@ -814,8 +582,6 @@ static irqreturn_t schizo_ue_intr(int irq, void *dev_id, struct pt_regs *regs) /* Interrogate IOMMU for error status. */ schizo_check_iommu_error(p, UE_ERR); - schizo_clear_other_err_intr(p, irq); - return IRQ_HANDLED; } @@ -905,8 +671,6 @@ static irqreturn_t schizo_ce_intr(int irq, void *dev_id, struct pt_regs *regs) printk("(none)"); printk("]\n"); - schizo_clear_other_err_intr(p, irq); - return IRQ_HANDLED; } @@ -1127,8 +891,6 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id, struct pt_regs *reg if (error_bits & (SCHIZO_PCIAFSR_PPERR | SCHIZO_PCIAFSR_SPERR)) pci_scan_for_parity_error(p, pbm, pbm->pci_bus); - schizo_clear_other_err_intr(p, irq); - return IRQ_HANDLED; } @@ -1184,7 +946,6 @@ static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id, struct pt_regs * printk("PCI%d: Unexpected Safari/JBUS error interrupt, errlog[%016lx]\n", p->index, errlog); - schizo_clear_other_err_intr(p, irq); return IRQ_HANDLED; } @@ -1192,7 +953,6 @@ static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id, struct pt_regs * p->index); schizo_check_iommu_error(p, SAFARI_ERR); - schizo_clear_other_err_intr(p, irq); return IRQ_HANDLED; } @@ -1224,77 +984,47 @@ static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id, struct pt_regs * static void tomatillo_register_error_handlers(struct pci_controller_info *p) { struct pci_pbm_info *pbm; - unsigned int irq; - struct ino_bucket *bucket; + struct of_device *op; u64 tmp, err_mask, err_no_mask; - /* Build IRQs and register handlers. */ + /* Tomatillo IRQ property layout is: + * 0: PCIERR + * 1: UE ERR + * 2: CE ERR + * 3: SERR + * 4: POWER FAIL? + */ + pbm = pbm_for_ino(p, SCHIZO_UE_INO); - irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_UE_INO); - if (request_irq(irq, schizo_ue_intr, - SA_SHIRQ, "TOMATILLO UE", p) < 0) { - prom_printf("%s: Cannot register UE interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + - schizo_imap_offset(SCHIZO_UE_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[1], schizo_ue_intr, IRQF_SHARED, + "TOMATILLO_UE", p); pbm = pbm_for_ino(p, SCHIZO_CE_INO); - irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_CE_INO); - if (request_irq(irq, schizo_ce_intr, - SA_SHIRQ, "TOMATILLO CE", p) < 0) { - prom_printf("%s: Cannot register CE interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + - schizo_imap_offset(SCHIZO_CE_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[2], schizo_ce_intr, IRQF_SHARED, + "TOMATILLO CE", p); pbm = pbm_for_ino(p, SCHIZO_PCIERR_A_INO); - irq = schizo_irq_build(pbm, NULL, ((pbm->portid << 6) | - SCHIZO_PCIERR_A_INO)); - if (request_irq(irq, schizo_pcierr_intr, - SA_SHIRQ, "TOMATILLO PCIERR", pbm) < 0) { - prom_printf("%s: Cannot register PBM A PciERR interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + - schizo_imap_offset(SCHIZO_PCIERR_A_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[0], schizo_pcierr_intr, IRQF_SHARED, + "TOMATILLO PCIERR-A", pbm); + pbm = pbm_for_ino(p, SCHIZO_PCIERR_B_INO); - irq = schizo_irq_build(pbm, NULL, ((pbm->portid << 6) | - SCHIZO_PCIERR_B_INO)); - if (request_irq(irq, schizo_pcierr_intr, - SA_SHIRQ, "TOMATILLO PCIERR", pbm) < 0) { - prom_printf("%s: Cannot register PBM B PciERR interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + - schizo_imap_offset(SCHIZO_PCIERR_B_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[0], schizo_pcierr_intr, IRQF_SHARED, + "TOMATILLO PCIERR-B", pbm); pbm = pbm_for_ino(p, SCHIZO_SERR_INO); - irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_SERR_INO); - if (request_irq(irq, schizo_safarierr_intr, - SA_SHIRQ, "TOMATILLO SERR", p) < 0) { - prom_printf("%s: Cannot register SafariERR interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + - schizo_imap_offset(SCHIZO_SERR_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[3], schizo_safarierr_intr, IRQF_SHARED, + "TOMATILLO SERR", p); /* Enable UE and CE interrupts for controller. */ schizo_write(p->pbm_A.controller_regs + SCHIZO_ECC_CTRL, @@ -1362,70 +1092,47 @@ static void tomatillo_register_error_handlers(struct pci_controller_info *p) static void schizo_register_error_handlers(struct pci_controller_info *p) { struct pci_pbm_info *pbm; - unsigned int irq; - struct ino_bucket *bucket; + struct of_device *op; u64 tmp, err_mask, err_no_mask; - /* Build IRQs and register handlers. */ + /* Schizo IRQ property layout is: + * 0: PCIERR + * 1: UE ERR + * 2: CE ERR + * 3: SERR + * 4: POWER FAIL? + */ + pbm = pbm_for_ino(p, SCHIZO_UE_INO); - irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_UE_INO); - if (request_irq(irq, schizo_ue_intr, - SA_SHIRQ, "SCHIZO UE", p) < 0) { - prom_printf("%s: Cannot register UE interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_UE_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[1], schizo_ue_intr, IRQF_SHARED, + "SCHIZO_UE", p); pbm = pbm_for_ino(p, SCHIZO_CE_INO); - irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_CE_INO); - if (request_irq(irq, schizo_ce_intr, - SA_SHIRQ, "SCHIZO CE", p) < 0) { - prom_printf("%s: Cannot register CE interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_CE_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[2], schizo_ce_intr, IRQF_SHARED, + "SCHIZO CE", p); pbm = pbm_for_ino(p, SCHIZO_PCIERR_A_INO); - irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_PCIERR_A_INO); - if (request_irq(irq, schizo_pcierr_intr, - SA_SHIRQ, "SCHIZO PCIERR", pbm) < 0) { - prom_printf("%s: Cannot register PBM A PciERR interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_PCIERR_A_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[0], schizo_pcierr_intr, IRQF_SHARED, + "SCHIZO PCIERR-A", pbm); + pbm = pbm_for_ino(p, SCHIZO_PCIERR_B_INO); - irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_PCIERR_B_INO); - if (request_irq(irq, schizo_pcierr_intr, - SA_SHIRQ, "SCHIZO PCIERR", &p->pbm_B) < 0) { - prom_printf("%s: Cannot register PBM B PciERR interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_PCIERR_B_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[0], schizo_pcierr_intr, IRQF_SHARED, + "SCHIZO PCIERR-B", pbm); pbm = pbm_for_ino(p, SCHIZO_SERR_INO); - irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_SERR_INO); - if (request_irq(irq, schizo_safarierr_intr, - SA_SHIRQ, "SCHIZO SERR", p) < 0) { - prom_printf("%s: Cannot register SafariERR interrupt.\n", - pbm->name); - prom_halt(); - } - bucket = __bucket(irq); - tmp = upa_readl(bucket->imap); - upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_SERR_INO) + 4)); + op = of_find_device_by_node(pbm->prom_node); + if (op) + request_irq(op->irqs[3], schizo_safarierr_intr, IRQF_SHARED, + "SCHIZO SERR", p); /* Enable UE and CE interrupts for controller. */ schizo_write(p->pbm_A.controller_regs + SCHIZO_ECC_CTRL, @@ -1560,10 +1267,12 @@ static void __schizo_scan_bus(struct pci_controller_info *p, pbm_config_busmastering(&p->pbm_B); p->pbm_B.is_66mhz_capable = - prom_getbool(p->pbm_B.prom_node, "66mhz-capable"); + (of_find_property(p->pbm_B.prom_node, "66mhz-capable", NULL) + != NULL); pbm_config_busmastering(&p->pbm_A); p->pbm_A.is_66mhz_capable = - prom_getbool(p->pbm_A.prom_node, "66mhz-capable"); + (of_find_property(p->pbm_A.prom_node, "66mhz-capable", NULL) + != NULL); pbm_scan_bus(p, &p->pbm_B); pbm_scan_bus(p, &p->pbm_A); @@ -1765,13 +1474,18 @@ static void schizo_pbm_iommu_init(struct pci_pbm_info *pbm) { struct pci_iommu *iommu = pbm->iommu; unsigned long i, tagbase, database; + struct property *prop; u32 vdma[2], dma_mask; u64 control; - int err, tsbsize; + int tsbsize; + + prop = of_find_property(pbm->prom_node, "virtual-dma", NULL); + if (prop) { + u32 *val = prop->value; - err = prom_getproperty(pbm->prom_node, "virtual-dma", - (char *)&vdma[0], sizeof(vdma)); - if (err == 0 || err == -1) { + vdma[0] = val[0]; + vdma[1] = val[1]; + } else { /* No property, use default values. */ vdma[0] = 0xc0000000; vdma[1] = 0x40000000; @@ -1882,6 +1596,7 @@ static void schizo_pbm_iommu_init(struct pci_pbm_info *pbm) static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) { + struct property *prop; u64 tmp; schizo_write(pbm->pbm_regs + SCHIZO_PCI_IRQ_RETRY, 5); @@ -1895,7 +1610,8 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) pbm->chip_version >= 0x2) tmp |= 0x3UL << SCHIZO_PCICTRL_PTO_SHIFT; - if (!prom_getbool(pbm->prom_node, "no-bus-parking")) + prop = of_find_property(pbm->prom_node, "no-bus-parking", NULL); + if (!prop) tmp |= SCHIZO_PCICTRL_PARK; else tmp &= ~SCHIZO_PCICTRL_PARK; @@ -1935,16 +1651,17 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) } static void schizo_pbm_init(struct pci_controller_info *p, - int prom_node, u32 portid, + struct device_node *dp, u32 portid, int chip_type) { - struct linux_prom64_registers pr_regs[4]; - unsigned int busrange[2]; + struct linux_prom64_registers *regs; + struct property *prop; + unsigned int *busrange; struct pci_pbm_info *pbm; const char *chipset_name; - u32 ino_bitmap[2]; + u32 *ino_bitmap; int is_pbm_a; - int err; + int len; switch (chip_type) { case PBM_CHIP_TYPE_TOMATILLO: @@ -1972,16 +1689,10 @@ static void schizo_pbm_init(struct pci_controller_info *p, * 3) PBM PCI config space * 4) Ichip regs */ - err = prom_getproperty(prom_node, "reg", - (char *)&pr_regs[0], - sizeof(pr_regs)); - if (err == 0 || err == -1) { - prom_printf("%s: Fatal error, no reg property.\n", - chipset_name); - prom_halt(); - } + prop = of_find_property(dp, "reg", NULL); + regs = prop->value; - is_pbm_a = ((pr_regs[0].phys_addr & 0x00700000) == 0x00600000); + is_pbm_a = ((regs[0].phys_addr & 0x00700000) == 0x00600000); if (is_pbm_a) pbm = &p->pbm_A; @@ -1990,92 +1701,62 @@ static void schizo_pbm_init(struct pci_controller_info *p, pbm->portid = portid; pbm->parent = p; - pbm->prom_node = prom_node; + pbm->prom_node = dp; pbm->pci_first_slot = 1; pbm->chip_type = chip_type; - pbm->chip_version = - prom_getintdefault(prom_node, "version#", 0); - pbm->chip_revision = - prom_getintdefault(prom_node, "module-revision#", 0); - - pbm->pbm_regs = pr_regs[0].phys_addr; - pbm->controller_regs = pr_regs[1].phys_addr - 0x10000UL; + pbm->chip_version = 0; + prop = of_find_property(dp, "version#", NULL); + if (prop) + pbm->chip_version = *(int *) prop->value; + pbm->chip_revision = 0; + prop = of_find_property(dp, "module-revision#", NULL); + if (prop) + pbm->chip_revision = *(int *) prop->value; + + pbm->pbm_regs = regs[0].phys_addr; + pbm->controller_regs = regs[1].phys_addr - 0x10000UL; if (chip_type == PBM_CHIP_TYPE_TOMATILLO) - pbm->sync_reg = pr_regs[3].phys_addr + 0x1a18UL; + pbm->sync_reg = regs[3].phys_addr + 0x1a18UL; - sprintf(pbm->name, - (chip_type == PBM_CHIP_TYPE_TOMATILLO ? - "TOMATILLO%d PBM%c" : - "SCHIZO%d PBM%c"), - p->index, - (pbm == &p->pbm_A ? 'A' : 'B')); + pbm->name = dp->full_name; - printk("%s: ver[%x:%x], portid %x, " - "cregs[%lx] pregs[%lx]\n", + printk("%s: %s PCI Bus Module ver[%x:%x]\n", pbm->name, - pbm->chip_version, pbm->chip_revision, - pbm->portid, - pbm->controller_regs, - pbm->pbm_regs); + (chip_type == PBM_CHIP_TYPE_TOMATILLO ? + "TOMATILLO" : "SCHIZO"), + pbm->chip_version, pbm->chip_revision); schizo_pbm_hw_init(pbm); - prom_getstring(prom_node, "name", - pbm->prom_name, - sizeof(pbm->prom_name)); - - err = prom_getproperty(prom_node, "ranges", - (char *) pbm->pbm_ranges, - sizeof(pbm->pbm_ranges)); - if (err == 0 || err == -1) { - prom_printf("%s: Fatal error, no ranges property.\n", - pbm->name); - prom_halt(); - } - + prop = of_find_property(dp, "ranges", &len); + pbm->pbm_ranges = prop->value; pbm->num_pbm_ranges = - (err / sizeof(struct linux_prom_pci_ranges)); + (len / sizeof(struct linux_prom_pci_ranges)); schizo_determine_mem_io_space(pbm); pbm_register_toplevel_resources(p, pbm); - err = prom_getproperty(prom_node, "interrupt-map", - (char *)pbm->pbm_intmap, - sizeof(pbm->pbm_intmap)); - if (err != -1) { - pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); - err = prom_getproperty(prom_node, "interrupt-map-mask", - (char *)&pbm->pbm_intmask, - sizeof(pbm->pbm_intmask)); - if (err == -1) { - prom_printf("%s: Fatal error, no " - "interrupt-map-mask.\n", pbm->name); - prom_halt(); - } + prop = of_find_property(dp, "interrupt-map", &len); + if (prop) { + pbm->pbm_intmap = prop->value; + pbm->num_pbm_intmap = + (len / sizeof(struct linux_prom_pci_intmap)); + + prop = of_find_property(dp, "interrupt-map-mask", NULL); + pbm->pbm_intmask = prop->value; } else { pbm->num_pbm_intmap = 0; - memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); } - err = prom_getproperty(prom_node, "ino-bitmap", - (char *) &ino_bitmap[0], - sizeof(ino_bitmap)); - if (err == 0 || err == -1) { - prom_printf("%s: Fatal error, no ino-bitmap.\n", pbm->name); - prom_halt(); - } + prop = of_find_property(dp, "ino-bitmap", NULL); + ino_bitmap = prop->value; pbm->ino_bitmap = (((u64)ino_bitmap[1] << 32UL) | ((u64)ino_bitmap[0] << 0UL)); - err = prom_getproperty(prom_node, "bus-range", - (char *)&busrange[0], - sizeof(busrange)); - if (err == 0 || err == -1) { - prom_printf("%s: Fatal error, no bus-range.\n", pbm->name); - prom_halt(); - } + prop = of_find_property(dp, "bus-range", NULL); + busrange = prop->value; pbm->pci_first_busno = busrange[0]; pbm->pci_last_busno = busrange[1]; @@ -2093,16 +1774,20 @@ static inline int portid_compare(u32 x, u32 y, int chip_type) return (x == y); } -static void __schizo_init(int node, char *model_name, int chip_type) +static void __schizo_init(struct device_node *dp, char *model_name, int chip_type) { struct pci_controller_info *p; struct pci_iommu *iommu; + struct property *prop; int is_pbm_a; u32 portid; - portid = prom_getintdefault(node, "portid", 0xff); + portid = 0xff; + prop = of_find_property(dp, "portid", NULL); + if (prop) + portid = *(u32 *) prop->value; - for(p = pci_controller_root; p; p = p->next) { + for (p = pci_controller_root; p; p = p->next) { struct pci_pbm_info *pbm; if (p->pbm_A.prom_node && p->pbm_B.prom_node) @@ -2113,8 +1798,8 @@ static void __schizo_init(int node, char *model_name, int chip_type) &p->pbm_B); if (portid_compare(pbm->portid, portid, chip_type)) { - is_pbm_a = (p->pbm_A.prom_node == 0); - schizo_pbm_init(p, node, portid, chip_type); + is_pbm_a = (p->pbm_A.prom_node == NULL); + schizo_pbm_init(p, dp, portid, chip_type); return; } } @@ -2147,7 +1832,6 @@ static void __schizo_init(int node, char *model_name, int chip_type) p->scan_bus = (chip_type == PBM_CHIP_TYPE_TOMATILLO ? tomatillo_scan_bus : schizo_scan_bus); - p->irq_build = schizo_irq_build; p->base_address_update = schizo_base_address_update; p->resource_adjust = schizo_resource_adjust; p->pci_ops = &schizo_ops; @@ -2155,20 +1839,20 @@ static void __schizo_init(int node, char *model_name, int chip_type) /* Like PSYCHO we have a 2GB aligned area for memory space. */ pci_memspace_mask = 0x7fffffffUL; - schizo_pbm_init(p, node, portid, chip_type); + schizo_pbm_init(p, dp, portid, chip_type); } -void schizo_init(int node, char *model_name) +void schizo_init(struct device_node *dp, char *model_name) { - __schizo_init(node, model_name, PBM_CHIP_TYPE_SCHIZO); + __schizo_init(dp, model_name, PBM_CHIP_TYPE_SCHIZO); } -void schizo_plus_init(int node, char *model_name) +void schizo_plus_init(struct device_node *dp, char *model_name) { - __schizo_init(node, model_name, PBM_CHIP_TYPE_SCHIZO_PLUS); + __schizo_init(dp, model_name, PBM_CHIP_TYPE_SCHIZO_PLUS); } -void tomatillo_init(int node, char *model_name) +void tomatillo_init(struct device_node *dp, char *model_name) { - __schizo_init(node, model_name, PBM_CHIP_TYPE_TOMATILLO); + __schizo_init(dp, model_name, PBM_CHIP_TYPE_TOMATILLO); } diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index 0c089520297..03ad4c06758 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c @@ -18,6 +18,7 @@ #include <asm/pstate.h> #include <asm/oplib.h> #include <asm/hypervisor.h> +#include <asm/prom.h> #include "pci_impl.h" #include "iommu_common.h" @@ -646,35 +647,37 @@ static int pdev_htab_add(u32 devhandle, unsigned int bus, unsigned int device, u /* Recursively descend into the OBP device tree, rooted at toplevel_node, * looking for a PCI device matching bus and devfn. */ -static int obp_find(struct linux_prom_pci_registers *pregs, int toplevel_node, unsigned int bus, unsigned int devfn) +static int obp_find(struct device_node *toplevel_node, unsigned int bus, unsigned int devfn) { - toplevel_node = prom_getchild(toplevel_node); + toplevel_node = toplevel_node->child; - while (toplevel_node != 0) { - int ret = obp_find(pregs, toplevel_node, bus, devfn); + while (toplevel_node != NULL) { + struct linux_prom_pci_registers *regs; + struct property *prop; + int ret; + ret = obp_find(toplevel_node, bus, devfn); if (ret != 0) return ret; - ret = prom_getproperty(toplevel_node, "reg", (char *) pregs, - sizeof(*pregs) * PROMREG_MAX); - if (ret == 0 || ret == -1) + prop = of_find_property(toplevel_node, "reg", NULL); + if (!prop) goto next_sibling; - if (((pregs[0].phys_hi >> 16) & 0xff) == bus && - ((pregs[0].phys_hi >> 8) & 0xff) == devfn) + regs = prop->value; + if (((regs->phys_hi >> 16) & 0xff) == bus && + ((regs->phys_hi >> 8) & 0xff) == devfn) break; next_sibling: - toplevel_node = prom_getsibling(toplevel_node); + toplevel_node = toplevel_node->sibling; } - return toplevel_node; + return toplevel_node != NULL; } static int pdev_htab_populate(struct pci_pbm_info *pbm) { - struct linux_prom_pci_registers pr[PROMREG_MAX]; u32 devhandle = pbm->devhandle; unsigned int bus; @@ -685,7 +688,7 @@ static int pdev_htab_populate(struct pci_pbm_info *pbm) unsigned int device = PCI_SLOT(devfn); unsigned int func = PCI_FUNC(devfn); - if (obp_find(pr, pbm->prom_node, bus, devfn)) { + if (obp_find(pbm->prom_node, bus, devfn)) { int err = pdev_htab_add(devhandle, bus, device, func); if (err) @@ -811,8 +814,7 @@ static void pbm_scan_bus(struct pci_controller_info *p, pci_fixup_host_bridge_self(pbm->pci_bus); pbm->pci_bus->self->sysdata = cookie; #endif - pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, - pbm->prom_node); + pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node); pci_record_assignments(pbm, pbm->pci_bus); pci_assign_unassigned(pbm, pbm->pci_bus); pci_fixup_irq(pbm, pbm->pci_bus); @@ -822,15 +824,18 @@ static void pbm_scan_bus(struct pci_controller_info *p, static void pci_sun4v_scan_bus(struct pci_controller_info *p) { - if (p->pbm_A.prom_node) { - p->pbm_A.is_66mhz_capable = - prom_getbool(p->pbm_A.prom_node, "66mhz-capable"); + struct property *prop; + struct device_node *dp; + + if ((dp = p->pbm_A.prom_node) != NULL) { + prop = of_find_property(dp, "66mhz-capable", NULL); + p->pbm_A.is_66mhz_capable = (prop != NULL); pbm_scan_bus(p, &p->pbm_A); } - if (p->pbm_B.prom_node) { - p->pbm_B.is_66mhz_capable = - prom_getbool(p->pbm_B.prom_node, "66mhz-capable"); + if ((dp = p->pbm_B.prom_node) != NULL) { + prop = of_find_property(dp, "66mhz-capable", NULL); + p->pbm_B.is_66mhz_capable = (prop != NULL); pbm_scan_bus(p, &p->pbm_B); } @@ -838,45 +843,6 @@ static void pci_sun4v_scan_bus(struct pci_controller_info *p) /* XXX register error interrupt handlers XXX */ } -static unsigned int pci_sun4v_irq_build(struct pci_pbm_info *pbm, - struct pci_dev *pdev, - unsigned int devino) -{ - u32 devhandle = pbm->devhandle; - int pil; - - pil = 5; - if (pdev) { - switch ((pdev->class >> 16) & 0xff) { - case PCI_BASE_CLASS_STORAGE: - pil = 5; - break; - - case PCI_BASE_CLASS_NETWORK: - pil = 6; - break; - - case PCI_BASE_CLASS_DISPLAY: - pil = 9; - break; - - case PCI_BASE_CLASS_MULTIMEDIA: - case PCI_BASE_CLASS_MEMORY: - case PCI_BASE_CLASS_BRIDGE: - case PCI_BASE_CLASS_SERIAL: - pil = 10; - break; - - default: - pil = 5; - break; - }; - } - BUG_ON(PIL_RESERVED(pil)); - - return sun4v_build_irq(devhandle, devino, pil, IBF_PCI); -} - static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource) { struct pcidev_cookie *pcp = pdev->sysdata; @@ -1012,8 +978,13 @@ static unsigned long probe_existing_entries(struct pci_pbm_info *pbm, HV_PCI_TSBID(0, i), &io_attrs, &ra); if (ret == HV_EOK) { - cnt++; - __set_bit(i, arena->map); + if (page_in_phys_avail(ra)) { + pci_sun4v_iommu_demap(devhandle, + HV_PCI_TSBID(0, i), 1); + } else { + cnt++; + __set_bit(i, arena->map); + } } } @@ -1023,13 +994,18 @@ static unsigned long probe_existing_entries(struct pci_pbm_info *pbm, static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm) { struct pci_iommu *iommu = pbm->iommu; + struct property *prop; unsigned long num_tsb_entries, sz; u32 vdma[2], dma_mask, dma_offset; - int err, tsbsize; + int tsbsize; + + prop = of_find_property(pbm->prom_node, "virtual-dma", NULL); + if (prop) { + u32 *val = prop->value; - err = prom_getproperty(pbm->prom_node, "virtual-dma", - (char *)&vdma[0], sizeof(vdma)); - if (err == 0 || err == -1) { + vdma[0] = val[0]; + vdma[1] = val[1]; + } else { /* No property, use default values. */ vdma[0] = 0x80000000; vdma[1] = 0x80000000; @@ -1081,34 +1057,30 @@ static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm) iommu->arena.limit = num_tsb_entries; sz = probe_existing_entries(pbm, iommu); - - printk("%s: TSB entries [%lu], existing mapings [%lu]\n", - pbm->name, num_tsb_entries, sz); + if (sz) + printk("%s: Imported %lu TSB entries from OBP\n", + pbm->name, sz); } static void pci_sun4v_get_bus_range(struct pci_pbm_info *pbm) { - unsigned int busrange[2]; - int prom_node = pbm->prom_node; - int err; - - err = prom_getproperty(prom_node, "bus-range", - (char *)&busrange[0], - sizeof(busrange)); - if (err == 0 || err == -1) { - prom_printf("%s: Fatal error, no bus-range.\n", pbm->name); - prom_halt(); - } + struct property *prop; + unsigned int *busrange; + + prop = of_find_property(pbm->prom_node, "bus-range", NULL); + + busrange = prop->value; pbm->pci_first_busno = busrange[0]; pbm->pci_last_busno = busrange[1]; } -static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node, u32 devhandle) +static void pci_sun4v_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 devhandle) { struct pci_pbm_info *pbm; - int err, i; + struct property *prop; + int len, i; if (devhandle & 0x40) pbm = &p->pbm_B; @@ -1116,32 +1088,19 @@ static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node, u32 pbm = &p->pbm_A; pbm->parent = p; - pbm->prom_node = prom_node; + pbm->prom_node = dp; pbm->pci_first_slot = 1; pbm->devhandle = devhandle; - sprintf(pbm->name, "SUN4V-PCI%d PBM%c", - p->index, (pbm == &p->pbm_A ? 'A' : 'B')); + pbm->name = dp->full_name; - printk("%s: devhandle[%x] prom_node[%x:%x]\n", - pbm->name, pbm->devhandle, - pbm->prom_node, prom_getchild(pbm->prom_node)); - - prom_getstring(prom_node, "name", - pbm->prom_name, sizeof(pbm->prom_name)); - - err = prom_getproperty(prom_node, "ranges", - (char *) pbm->pbm_ranges, - sizeof(pbm->pbm_ranges)); - if (err == 0 || err == -1) { - prom_printf("%s: Fatal error, no ranges property.\n", - pbm->name); - prom_halt(); - } + printk("%s: SUN4V PCI Bus Module\n", pbm->name); + prop = of_find_property(dp, "ranges", &len); + pbm->pbm_ranges = prop->value; pbm->num_pbm_ranges = - (err / sizeof(struct linux_prom_pci_ranges)); + (len / sizeof(struct linux_prom_pci_ranges)); /* Mask out the top 8 bits of the ranges, leaving the real * physical address. @@ -1152,24 +1111,13 @@ static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node, u32 pci_sun4v_determine_mem_io_space(pbm); pbm_register_toplevel_resources(p, pbm); - err = prom_getproperty(prom_node, "interrupt-map", - (char *)pbm->pbm_intmap, - sizeof(pbm->pbm_intmap)); - if (err == 0 || err == -1) { - prom_printf("%s: Fatal error, no interrupt-map property.\n", - pbm->name); - prom_halt(); - } + prop = of_find_property(dp, "interrupt-map", &len); + pbm->pbm_intmap = prop->value; + pbm->num_pbm_intmap = + (len / sizeof(struct linux_prom_pci_intmap)); - pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); - err = prom_getproperty(prom_node, "interrupt-map-mask", - (char *)&pbm->pbm_intmask, - sizeof(pbm->pbm_intmask)); - if (err == 0 || err == -1) { - prom_printf("%s: Fatal error, no interrupt-map-mask.\n", - pbm->name); - prom_halt(); - } + prop = of_find_property(dp, "interrupt-map-mask", NULL); + pbm->pbm_intmask = prop->value; pci_sun4v_get_bus_range(pbm); pci_sun4v_iommu_init(pbm); @@ -1177,16 +1125,19 @@ static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node, u32 pdev_htab_populate(pbm); } -void sun4v_pci_init(int node, char *model_name) +void sun4v_pci_init(struct device_node *dp, char *model_name) { struct pci_controller_info *p; struct pci_iommu *iommu; - struct linux_prom64_registers regs; + struct property *prop; + struct linux_prom64_registers *regs; u32 devhandle; int i; - prom_getproperty(node, "reg", (char *)®s, sizeof(regs)); - devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff; + prop = of_find_property(dp, "reg", NULL); + regs = prop->value; + + devhandle = (regs->phys_addr >> 32UL) & 0x0fffffff; for (p = pci_controller_root; p; p = p->next) { struct pci_pbm_info *pbm; @@ -1199,7 +1150,7 @@ void sun4v_pci_init(int node, char *model_name) &p->pbm_B); if (pbm->devhandle == (devhandle ^ 0x40)) { - pci_sun4v_pbm_init(p, node, devhandle); + pci_sun4v_pbm_init(p, dp, devhandle); return; } } @@ -1240,7 +1191,6 @@ void sun4v_pci_init(int node, char *model_name) p->pbms_same_domain = 0; p->scan_bus = pci_sun4v_scan_bus; - p->irq_build = pci_sun4v_irq_build; p->base_address_update = pci_sun4v_base_address_update; p->resource_adjust = pci_sun4v_resource_adjust; p->pci_ops = &pci_sun4v_ops; @@ -1250,7 +1200,7 @@ void sun4v_pci_init(int node, char *model_name) */ pci_memspace_mask = 0x7fffffffUL; - pci_sun4v_pbm_init(p, node, devhandle); + pci_sun4v_pbm_init(p, dp, devhandle); return; fatal_memory_error: diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c index 30bcaf58e3a..e55466c77b6 100644 --- a/arch/sparc64/kernel/power.c +++ b/arch/sparc64/kernel/power.c @@ -6,7 +6,6 @@ #define __KERNEL_SYSCALLS__ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -17,9 +16,10 @@ #include <linux/pm.h> #include <asm/system.h> -#include <asm/ebus.h> -#include <asm/isa.h> #include <asm/auxio.h> +#include <asm/prom.h> +#include <asm/of_device.h> +#include <asm/io.h> #include <linux/unistd.h> @@ -30,6 +30,7 @@ int scons_pwroff = 1; #ifdef CONFIG_PCI +#include <linux/pci.h> static void __iomem *power_reg; static DECLARE_WAIT_QUEUE_HEAD(powerd_wait); @@ -105,87 +106,61 @@ again: return 0; } -static int __init has_button_interrupt(unsigned int irq, int prom_node) +static int __init has_button_interrupt(unsigned int irq, struct device_node *dp) { if (irq == PCI_IRQ_NONE) return 0; - if (!prom_node_has_property(prom_node, "button")) + if (!of_find_property(dp, "button", NULL)) return 0; return 1; } -static int __init power_probe_ebus(struct resource **resp, unsigned int *irq_p, int *prom_node_p) +static int __devinit power_probe(struct of_device *op, const struct of_device_id *match) { - struct linux_ebus *ebus; - struct linux_ebus_device *edev; - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_name, "power")) { - *resp = &edev->resource[0]; - *irq_p = edev->irqs[0]; - *prom_node_p = edev->prom_node; - return 0; - } - } - } - return -ENODEV; -} - -static int __init power_probe_isa(struct resource **resp, unsigned int *irq_p, int *prom_node_p) -{ - struct sparc_isa_bridge *isa_bus; - struct sparc_isa_device *isa_dev; - - for_each_isa(isa_bus) { - for_each_isadev(isa_dev, isa_bus) { - if (!strcmp(isa_dev->prom_name, "power")) { - *resp = &isa_dev->resource; - *irq_p = isa_dev->irq; - *prom_node_p = isa_dev->prom_node; - return 0; - } - } - } - return -ENODEV; -} - -void __init power_init(void) -{ - struct resource *res = NULL; - unsigned int irq; - int prom_node; - static int invoked; + struct resource *res = &op->resource[0]; + unsigned int irq= op->irqs[0]; - if (invoked) - return; - invoked = 1; + power_reg = of_ioremap(res, 0, 0x4, "power"); - if (!power_probe_ebus(&res, &irq, &prom_node)) - goto found; + printk("%s: Control reg at %lx ... ", + op->node->name, res->start); - if (!power_probe_isa(&res, &irq, &prom_node)) - goto found; - - return; - -found: - power_reg = ioremap(res->start, 0x4); - printk("power: Control reg at %p ... ", power_reg); poweroff_method = machine_halt; /* able to use the standard halt */ - if (has_button_interrupt(irq, prom_node)) { + + if (has_button_interrupt(irq, op->node)) { if (kernel_thread(powerd, NULL, CLONE_FS) < 0) { printk("Failed to start power daemon.\n"); - return; + return 0; } printk("powerd running.\n"); if (request_irq(irq, - power_handler, SA_SHIRQ, "power", NULL) < 0) + power_handler, 0, "power", NULL) < 0) printk("power: Error, cannot register IRQ handler.\n"); } else { printk("not using powerd.\n"); } + + return 0; +} + +static struct of_device_id power_match[] = { + { + .name = "power", + }, + {}, +}; + +static struct of_platform_driver power_driver = { + .name = "power", + .match_table = power_match, + .probe = power_probe, +}; + +void __init power_init(void) +{ + of_register_driver(&power_driver, &of_bus_type); + return; } #endif /* CONFIG_PCI */ diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 1c7ca2f712d..7d75cd4eb29 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -12,7 +12,6 @@ #include <stdarg.h> -#include <linux/config.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/sched.h> @@ -26,7 +25,6 @@ #include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> -#include <linux/config.h> #include <linux/reboot.h> #include <linux/delay.h> #include <linux/compat.h> diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c new file mode 100644 index 00000000000..fa484d4f241 --- /dev/null +++ b/arch/sparc64/kernel/prom.c @@ -0,0 +1,1527 @@ +/* + * 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 + * + * Adapted for sparc64 by David S. Miller davem@davemloft.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/bootmem.h> +#include <linux/module.h> + +#include <asm/prom.h> +#include <asm/of_device.h> +#include <asm/oplib.h> +#include <asm/irq.h> +#include <asm/asi.h> +#include <asm/upa.h> + +static struct device_node *allnodes; + +/* use when traversing tree through the allnext, child, sibling, + * or parent members of struct device_node. + */ +static DEFINE_RWLOCK(devtree_lock); + +int of_device_is_compatible(struct device_node *device, const char *compat) +{ + const char* cp; + int cplen, l; + + cp = (char *) of_get_property(device, "compatible", &cplen); + if (cp == NULL) + return 0; + while (cplen > 0) { + if (strncmp(cp, compat, strlen(compat)) == 0) + return 1; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + } + + return 0; +} +EXPORT_SYMBOL(of_device_is_compatible); + +struct device_node *of_get_parent(const struct device_node *node) +{ + struct device_node *np; + + if (!node) + return NULL; + + np = node->parent; + + return np; +} +EXPORT_SYMBOL(of_get_parent); + +struct device_node *of_get_next_child(const struct device_node *node, + struct device_node *prev) +{ + struct device_node *next; + + next = prev ? prev->sibling : node->child; + for (; next != 0; next = next->sibling) { + break; + } + + return next; +} +EXPORT_SYMBOL(of_get_next_child); + +struct device_node *of_find_node_by_path(const char *path) +{ + struct device_node *np = allnodes; + + for (; np != 0; np = np->allnext) { + if (np->full_name != 0 && strcmp(np->full_name, path) == 0) + break; + } + + return np; +} +EXPORT_SYMBOL(of_find_node_by_path); + +struct device_node *of_find_node_by_phandle(phandle handle) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->node == handle) + break; + + return np; +} +EXPORT_SYMBOL(of_find_node_by_phandle); + +struct device_node *of_find_node_by_name(struct device_node *from, + const char *name) +{ + struct device_node *np; + + np = from ? from->allnext : allnodes; + for (; np != NULL; np = np->allnext) + if (np->name != NULL && strcmp(np->name, name) == 0) + break; + + return np; +} +EXPORT_SYMBOL(of_find_node_by_name); + +struct device_node *of_find_node_by_type(struct device_node *from, + const char *type) +{ + struct device_node *np; + + np = from ? from->allnext : allnodes; + for (; np != 0; np = np->allnext) + if (np->type != 0 && strcmp(np->type, type) == 0) + break; + + return np; +} +EXPORT_SYMBOL(of_find_node_by_type); + +struct device_node *of_find_compatible_node(struct device_node *from, + const char *type, const char *compatible) +{ + struct device_node *np; + + np = from ? from->allnext : allnodes; + for (; np != 0; np = np->allnext) { + if (type != NULL + && !(np->type != 0 && strcmp(np->type, type) == 0)) + continue; + if (of_device_is_compatible(np, compatible)) + break; + } + + return np; +} +EXPORT_SYMBOL(of_find_compatible_node); + +struct property *of_find_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; + break; + } + } + return pp; +} +EXPORT_SYMBOL(of_find_property); + +/* + * Find a property with a given name for a given node + * and return the value. + */ +void *of_get_property(struct device_node *np, const char *name, int *lenp) +{ + struct property *pp = of_find_property(np,name,lenp); + return pp ? pp->value : NULL; +} +EXPORT_SYMBOL(of_get_property); + +int of_getintprop_default(struct device_node *np, const char *name, int def) +{ + struct property *prop; + int len; + + prop = of_find_property(np, name, &len); + if (!prop || len != 4) + return def; + + return *(int *) prop->value; +} +EXPORT_SYMBOL(of_getintprop_default); + +int of_n_addr_cells(struct device_node *np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = of_get_property(np, "#address-cells", NULL); + if (ip != NULL) + return *ip; + } while (np->parent); + /* No #address-cells property for the root node, default to 2 */ + return 2; +} +EXPORT_SYMBOL(of_n_addr_cells); + +int of_n_size_cells(struct device_node *np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = of_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; +} +EXPORT_SYMBOL(of_n_size_cells); + +int of_set_property(struct device_node *dp, const char *name, void *val, int len) +{ + struct property **prevp; + void *new_val; + int err; + + new_val = kmalloc(len, GFP_KERNEL); + if (!new_val) + return -ENOMEM; + + memcpy(new_val, val, len); + + err = -ENODEV; + + write_lock(&devtree_lock); + prevp = &dp->properties; + while (*prevp) { + struct property *prop = *prevp; + + if (!strcmp(prop->name, name)) { + void *old_val = prop->value; + int ret; + + ret = prom_setprop(dp->node, name, val, len); + err = -EINVAL; + if (ret >= 0) { + prop->value = new_val; + prop->length = len; + + if (OF_IS_DYNAMIC(prop)) + kfree(old_val); + + OF_MARK_DYNAMIC(prop); + + err = 0; + } + break; + } + prevp = &(*prevp)->next; + } + write_unlock(&devtree_lock); + + /* XXX Upate procfs if necessary... */ + + return err; +} +EXPORT_SYMBOL(of_set_property); + +static unsigned int prom_early_allocated; + +static void * __init prom_early_alloc(unsigned long size) +{ + void *ret; + + ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL); + if (ret != NULL) + memset(ret, 0, size); + + prom_early_allocated += size; + + return ret; +} + +#ifdef CONFIG_PCI +/* PSYCHO interrupt mapping support. */ +#define PSYCHO_IMAP_A_SLOT0 0x0c00UL +#define PSYCHO_IMAP_B_SLOT0 0x0c20UL +static unsigned long psycho_pcislot_imap_offset(unsigned long ino) +{ + unsigned int bus = (ino & 0x10) >> 4; + unsigned int slot = (ino & 0x0c) >> 2; + + if (bus == 0) + return PSYCHO_IMAP_A_SLOT0 + (slot * 8); + else + return PSYCHO_IMAP_B_SLOT0 + (slot * 8); +} + +#define PSYCHO_IMAP_SCSI 0x1000UL +#define PSYCHO_IMAP_ETH 0x1008UL +#define PSYCHO_IMAP_BPP 0x1010UL +#define PSYCHO_IMAP_AU_REC 0x1018UL +#define PSYCHO_IMAP_AU_PLAY 0x1020UL +#define PSYCHO_IMAP_PFAIL 0x1028UL +#define PSYCHO_IMAP_KMS 0x1030UL +#define PSYCHO_IMAP_FLPY 0x1038UL +#define PSYCHO_IMAP_SHW 0x1040UL +#define PSYCHO_IMAP_KBD 0x1048UL +#define PSYCHO_IMAP_MS 0x1050UL +#define PSYCHO_IMAP_SER 0x1058UL +#define PSYCHO_IMAP_TIM0 0x1060UL +#define PSYCHO_IMAP_TIM1 0x1068UL +#define PSYCHO_IMAP_UE 0x1070UL +#define PSYCHO_IMAP_CE 0x1078UL +#define PSYCHO_IMAP_A_ERR 0x1080UL +#define PSYCHO_IMAP_B_ERR 0x1088UL +#define PSYCHO_IMAP_PMGMT 0x1090UL +#define PSYCHO_IMAP_GFX 0x1098UL +#define PSYCHO_IMAP_EUPA 0x10a0UL + +static unsigned long __psycho_onboard_imap_off[] = { +/*0x20*/ PSYCHO_IMAP_SCSI, +/*0x21*/ PSYCHO_IMAP_ETH, +/*0x22*/ PSYCHO_IMAP_BPP, +/*0x23*/ PSYCHO_IMAP_AU_REC, +/*0x24*/ PSYCHO_IMAP_AU_PLAY, +/*0x25*/ PSYCHO_IMAP_PFAIL, +/*0x26*/ PSYCHO_IMAP_KMS, +/*0x27*/ PSYCHO_IMAP_FLPY, +/*0x28*/ PSYCHO_IMAP_SHW, +/*0x29*/ PSYCHO_IMAP_KBD, +/*0x2a*/ PSYCHO_IMAP_MS, +/*0x2b*/ PSYCHO_IMAP_SER, +/*0x2c*/ PSYCHO_IMAP_TIM0, +/*0x2d*/ PSYCHO_IMAP_TIM1, +/*0x2e*/ PSYCHO_IMAP_UE, +/*0x2f*/ PSYCHO_IMAP_CE, +/*0x30*/ PSYCHO_IMAP_A_ERR, +/*0x31*/ PSYCHO_IMAP_B_ERR, +/*0x32*/ PSYCHO_IMAP_PMGMT +}; +#define PSYCHO_ONBOARD_IRQ_BASE 0x20 +#define PSYCHO_ONBOARD_IRQ_LAST 0x32 +#define psycho_onboard_imap_offset(__ino) \ + __psycho_onboard_imap_off[(__ino) - PSYCHO_ONBOARD_IRQ_BASE] + +#define PSYCHO_ICLR_A_SLOT0 0x1400UL +#define PSYCHO_ICLR_SCSI 0x1800UL + +#define psycho_iclr_offset(ino) \ + ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \ + (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3))) + +static unsigned int psycho_irq_build(struct device_node *dp, + unsigned int ino, + void *_data) +{ + unsigned long controller_regs = (unsigned long) _data; + unsigned long imap, iclr; + unsigned long imap_off, iclr_off; + int inofixup = 0; + + ino &= 0x3f; + if (ino < PSYCHO_ONBOARD_IRQ_BASE) { + /* PCI slot */ + imap_off = psycho_pcislot_imap_offset(ino); + } else { + /* Onboard device */ + if (ino > PSYCHO_ONBOARD_IRQ_LAST) { + prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino); + prom_halt(); + } + imap_off = psycho_onboard_imap_offset(ino); + } + + /* Now build the IRQ bucket. */ + imap = controller_regs + imap_off; + imap += 4; + + iclr_off = psycho_iclr_offset(ino); + iclr = controller_regs + iclr_off; + iclr += 4; + + if ((ino & 0x20) == 0) + inofixup = ino & 0x03; + + return build_irq(inofixup, iclr, imap); +} + +static void psycho_irq_trans_init(struct device_node *dp) +{ + struct linux_prom64_registers *regs; + + dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); + dp->irq_trans->irq_build = psycho_irq_build; + + regs = of_get_property(dp, "reg", NULL); + dp->irq_trans->data = (void *) regs[2].phys_addr; +} + +#define sabre_read(__reg) \ +({ u64 __ret; \ + __asm__ __volatile__("ldxa [%1] %2, %0" \ + : "=r" (__ret) \ + : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ + : "memory"); \ + __ret; \ +}) + +struct sabre_irq_data { + unsigned long controller_regs; + unsigned int pci_first_busno; +}; +#define SABRE_CONFIGSPACE 0x001000000UL +#define SABRE_WRSYNC 0x1c20UL + +#define SABRE_CONFIG_BASE(CONFIG_SPACE) \ + (CONFIG_SPACE | (1UL << 24)) +#define SABRE_CONFIG_ENCODE(BUS, DEVFN, REG) \ + (((unsigned long)(BUS) << 16) | \ + ((unsigned long)(DEVFN) << 8) | \ + ((unsigned long)(REG))) + +/* When a device lives behind a bridge deeper in the PCI bus topology + * than APB, a special sequence must run to make sure all pending DMA + * transfers at the time of IRQ delivery are visible in the coherency + * domain by the cpu. This sequence is to perform a read on the far + * side of the non-APB bridge, then perform a read of Sabre's DMA + * write-sync register. + */ +static void sabre_wsync_handler(unsigned int ino, void *_arg1, void *_arg2) +{ + unsigned int phys_hi = (unsigned int) (unsigned long) _arg1; + struct sabre_irq_data *irq_data = _arg2; + unsigned long controller_regs = irq_data->controller_regs; + unsigned long sync_reg = controller_regs + SABRE_WRSYNC; + unsigned long config_space = controller_regs + SABRE_CONFIGSPACE; + unsigned int bus, devfn; + u16 _unused; + + config_space = SABRE_CONFIG_BASE(config_space); + + bus = (phys_hi >> 16) & 0xff; + devfn = (phys_hi >> 8) & 0xff; + + config_space |= SABRE_CONFIG_ENCODE(bus, devfn, 0x00); + + __asm__ __volatile__("membar #Sync\n\t" + "lduha [%1] %2, %0\n\t" + "membar #Sync" + : "=r" (_unused) + : "r" ((u16 *) config_space), + "i" (ASI_PHYS_BYPASS_EC_E_L) + : "memory"); + + sabre_read(sync_reg); +} + +#define SABRE_IMAP_A_SLOT0 0x0c00UL +#define SABRE_IMAP_B_SLOT0 0x0c20UL +#define SABRE_IMAP_SCSI 0x1000UL +#define SABRE_IMAP_ETH 0x1008UL +#define SABRE_IMAP_BPP 0x1010UL +#define SABRE_IMAP_AU_REC 0x1018UL +#define SABRE_IMAP_AU_PLAY 0x1020UL +#define SABRE_IMAP_PFAIL 0x1028UL +#define SABRE_IMAP_KMS 0x1030UL +#define SABRE_IMAP_FLPY 0x1038UL +#define SABRE_IMAP_SHW 0x1040UL +#define SABRE_IMAP_KBD 0x1048UL +#define SABRE_IMAP_MS 0x1050UL +#define SABRE_IMAP_SER 0x1058UL +#define SABRE_IMAP_UE 0x1070UL +#define SABRE_IMAP_CE 0x1078UL +#define SABRE_IMAP_PCIERR 0x1080UL +#define SABRE_IMAP_GFX 0x1098UL +#define SABRE_IMAP_EUPA 0x10a0UL +#define SABRE_ICLR_A_SLOT0 0x1400UL +#define SABRE_ICLR_B_SLOT0 0x1480UL +#define SABRE_ICLR_SCSI 0x1800UL +#define SABRE_ICLR_ETH 0x1808UL +#define SABRE_ICLR_BPP 0x1810UL +#define SABRE_ICLR_AU_REC 0x1818UL +#define SABRE_ICLR_AU_PLAY 0x1820UL +#define SABRE_ICLR_PFAIL 0x1828UL +#define SABRE_ICLR_KMS 0x1830UL +#define SABRE_ICLR_FLPY 0x1838UL +#define SABRE_ICLR_SHW 0x1840UL +#define SABRE_ICLR_KBD 0x1848UL +#define SABRE_ICLR_MS 0x1850UL +#define SABRE_ICLR_SER 0x1858UL +#define SABRE_ICLR_UE 0x1870UL +#define SABRE_ICLR_CE 0x1878UL +#define SABRE_ICLR_PCIERR 0x1880UL + +static unsigned long sabre_pcislot_imap_offset(unsigned long ino) +{ + unsigned int bus = (ino & 0x10) >> 4; + unsigned int slot = (ino & 0x0c) >> 2; + + if (bus == 0) + return SABRE_IMAP_A_SLOT0 + (slot * 8); + else + return SABRE_IMAP_B_SLOT0 + (slot * 8); +} + +static unsigned long __sabre_onboard_imap_off[] = { +/*0x20*/ SABRE_IMAP_SCSI, +/*0x21*/ SABRE_IMAP_ETH, +/*0x22*/ SABRE_IMAP_BPP, +/*0x23*/ SABRE_IMAP_AU_REC, +/*0x24*/ SABRE_IMAP_AU_PLAY, +/*0x25*/ SABRE_IMAP_PFAIL, +/*0x26*/ SABRE_IMAP_KMS, +/*0x27*/ SABRE_IMAP_FLPY, +/*0x28*/ SABRE_IMAP_SHW, +/*0x29*/ SABRE_IMAP_KBD, +/*0x2a*/ SABRE_IMAP_MS, +/*0x2b*/ SABRE_IMAP_SER, +/*0x2c*/ 0 /* reserved */, +/*0x2d*/ 0 /* reserved */, +/*0x2e*/ SABRE_IMAP_UE, +/*0x2f*/ SABRE_IMAP_CE, +/*0x30*/ SABRE_IMAP_PCIERR, +}; +#define SABRE_ONBOARD_IRQ_BASE 0x20 +#define SABRE_ONBOARD_IRQ_LAST 0x30 +#define sabre_onboard_imap_offset(__ino) \ + __sabre_onboard_imap_off[(__ino) - SABRE_ONBOARD_IRQ_BASE] + +#define sabre_iclr_offset(ino) \ + ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \ + (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3))) + +static unsigned int sabre_irq_build(struct device_node *dp, + unsigned int ino, + void *_data) +{ + struct sabre_irq_data *irq_data = _data; + unsigned long controller_regs = irq_data->controller_regs; + struct linux_prom_pci_registers *regs; + unsigned long imap, iclr; + unsigned long imap_off, iclr_off; + int inofixup = 0; + int virt_irq; + + ino &= 0x3f; + if (ino < SABRE_ONBOARD_IRQ_BASE) { + /* PCI slot */ + imap_off = sabre_pcislot_imap_offset(ino); + } else { + /* onboard device */ + if (ino > SABRE_ONBOARD_IRQ_LAST) { + prom_printf("sabre_irq_build: Wacky INO [%x]\n", ino); + prom_halt(); + } + imap_off = sabre_onboard_imap_offset(ino); + } + + /* Now build the IRQ bucket. */ + imap = controller_regs + imap_off; + imap += 4; + + iclr_off = sabre_iclr_offset(ino); + iclr = controller_regs + iclr_off; + iclr += 4; + + if ((ino & 0x20) == 0) + inofixup = ino & 0x03; + + virt_irq = build_irq(inofixup, iclr, imap); + + regs = of_get_property(dp, "reg", NULL); + if (regs && + ((regs->phys_hi >> 16) & 0xff) != irq_data->pci_first_busno) { + irq_install_pre_handler(virt_irq, + sabre_wsync_handler, + (void *) (long) regs->phys_hi, + (void *) + controller_regs + + SABRE_WRSYNC); + } + + return virt_irq; +} + +static void sabre_irq_trans_init(struct device_node *dp) +{ + struct linux_prom64_registers *regs; + struct sabre_irq_data *irq_data; + u32 *busrange; + + dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); + dp->irq_trans->irq_build = sabre_irq_build; + + irq_data = prom_early_alloc(sizeof(struct sabre_irq_data)); + + regs = of_get_property(dp, "reg", NULL); + irq_data->controller_regs = regs[0].phys_addr; + + busrange = of_get_property(dp, "bus-range", NULL); + irq_data->pci_first_busno = busrange[0]; + + dp->irq_trans->data = irq_data; +} + +/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the + * imap/iclr registers are per-PBM. + */ +#define SCHIZO_IMAP_BASE 0x1000UL +#define SCHIZO_ICLR_BASE 0x1400UL + +static unsigned long schizo_imap_offset(unsigned long ino) +{ + return SCHIZO_IMAP_BASE + (ino * 8UL); +} + +static unsigned long schizo_iclr_offset(unsigned long ino) +{ + return SCHIZO_ICLR_BASE + (ino * 8UL); +} + +static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs, + unsigned int ino) +{ + return pbm_regs + schizo_iclr_offset(ino) + 4; +} + +static unsigned long schizo_ino_to_imap(unsigned long pbm_regs, + unsigned int ino) +{ + return pbm_regs + schizo_imap_offset(ino) + 4; +} + +#define schizo_read(__reg) \ +({ u64 __ret; \ + __asm__ __volatile__("ldxa [%1] %2, %0" \ + : "=r" (__ret) \ + : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ + : "memory"); \ + __ret; \ +}) +#define schizo_write(__reg, __val) \ + __asm__ __volatile__("stxa %0, [%1] %2" \ + : /* no outputs */ \ + : "r" (__val), "r" (__reg), \ + "i" (ASI_PHYS_BYPASS_EC_E) \ + : "memory") + +static void tomatillo_wsync_handler(unsigned int ino, void *_arg1, void *_arg2) +{ + unsigned long sync_reg = (unsigned long) _arg2; + u64 mask = 1UL << (ino & IMAP_INO); + u64 val; + int limit; + + schizo_write(sync_reg, mask); + + limit = 100000; + val = 0; + while (--limit) { + val = schizo_read(sync_reg); + if (!(val & mask)) + break; + } + if (limit <= 0) { + printk("tomatillo_wsync_handler: DMA won't sync [%lx:%lx]\n", + val, mask); + } + + if (_arg1) { + static unsigned char cacheline[64] + __attribute__ ((aligned (64))); + + __asm__ __volatile__("rd %%fprs, %0\n\t" + "or %0, %4, %1\n\t" + "wr %1, 0x0, %%fprs\n\t" + "stda %%f0, [%5] %6\n\t" + "wr %0, 0x0, %%fprs\n\t" + "membar #Sync" + : "=&r" (mask), "=&r" (val) + : "0" (mask), "1" (val), + "i" (FPRS_FEF), "r" (&cacheline[0]), + "i" (ASI_BLK_COMMIT_P)); + } +} + +struct schizo_irq_data { + unsigned long pbm_regs; + unsigned long sync_reg; + u32 portid; + int chip_version; +}; + +static unsigned int schizo_irq_build(struct device_node *dp, + unsigned int ino, + void *_data) +{ + struct schizo_irq_data *irq_data = _data; + unsigned long pbm_regs = irq_data->pbm_regs; + unsigned long imap, iclr; + int ign_fixup; + int virt_irq; + int is_tomatillo; + + ino &= 0x3f; + + /* Now build the IRQ bucket. */ + imap = schizo_ino_to_imap(pbm_regs, ino); + iclr = schizo_ino_to_iclr(pbm_regs, ino); + + /* On Schizo, no inofixup occurs. This is because each + * INO has it's own IMAP register. On Psycho and Sabre + * there is only one IMAP register for each PCI slot even + * though four different INOs can be generated by each + * PCI slot. + * + * But, for JBUS variants (essentially, Tomatillo), we have + * to fixup the lowest bit of the interrupt group number. + */ + ign_fixup = 0; + + is_tomatillo = (irq_data->sync_reg != 0UL); + + if (is_tomatillo) { + if (irq_data->portid & 1) + ign_fixup = (1 << 6); + } + + virt_irq = build_irq(ign_fixup, iclr, imap); + + if (is_tomatillo) { + irq_install_pre_handler(virt_irq, + tomatillo_wsync_handler, + ((irq_data->chip_version <= 4) ? + (void *) 1 : (void *) 0), + (void *) irq_data->sync_reg); + } + + return virt_irq; +} + +static void schizo_irq_trans_init(struct device_node *dp) +{ + struct linux_prom64_registers *regs; + struct schizo_irq_data *irq_data; + + dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); + dp->irq_trans->irq_build = schizo_irq_build; + + irq_data = prom_early_alloc(sizeof(struct schizo_irq_data)); + + regs = of_get_property(dp, "reg", NULL); + dp->irq_trans->data = irq_data; + + irq_data->pbm_regs = regs[0].phys_addr; + irq_data->sync_reg = regs[3].phys_addr + 0x1a18UL; + irq_data->portid = of_getintprop_default(dp, "portid", 0); + irq_data->chip_version = of_getintprop_default(dp, "version#", 0); +} + +static unsigned int pci_sun4v_irq_build(struct device_node *dp, + unsigned int devino, + void *_data) +{ + u32 devhandle = (u32) (unsigned long) _data; + + return sun4v_build_irq(devhandle, devino); +} + +static void pci_sun4v_irq_trans_init(struct device_node *dp) +{ + struct linux_prom64_registers *regs; + + dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); + dp->irq_trans->irq_build = pci_sun4v_irq_build; + + regs = of_get_property(dp, "reg", NULL); + dp->irq_trans->data = (void *) (unsigned long) + ((regs->phys_addr >> 32UL) & 0x0fffffff); +} +#endif /* CONFIG_PCI */ + +#ifdef CONFIG_SBUS +/* INO number to IMAP register offset for SYSIO external IRQ's. + * This should conform to both Sunfire/Wildfire server and Fusion + * desktop designs. + */ +#define SYSIO_IMAP_SLOT0 0x2c04UL +#define SYSIO_IMAP_SLOT1 0x2c0cUL +#define SYSIO_IMAP_SLOT2 0x2c14UL +#define SYSIO_IMAP_SLOT3 0x2c1cUL +#define SYSIO_IMAP_SCSI 0x3004UL +#define SYSIO_IMAP_ETH 0x300cUL +#define SYSIO_IMAP_BPP 0x3014UL +#define SYSIO_IMAP_AUDIO 0x301cUL +#define SYSIO_IMAP_PFAIL 0x3024UL +#define SYSIO_IMAP_KMS 0x302cUL +#define SYSIO_IMAP_FLPY 0x3034UL +#define SYSIO_IMAP_SHW 0x303cUL +#define SYSIO_IMAP_KBD 0x3044UL +#define SYSIO_IMAP_MS 0x304cUL +#define SYSIO_IMAP_SER 0x3054UL +#define SYSIO_IMAP_TIM0 0x3064UL +#define SYSIO_IMAP_TIM1 0x306cUL +#define SYSIO_IMAP_UE 0x3074UL +#define SYSIO_IMAP_CE 0x307cUL +#define SYSIO_IMAP_SBERR 0x3084UL +#define SYSIO_IMAP_PMGMT 0x308cUL +#define SYSIO_IMAP_GFX 0x3094UL +#define SYSIO_IMAP_EUPA 0x309cUL + +#define bogon ((unsigned long) -1) +static unsigned long sysio_irq_offsets[] = { + /* SBUS Slot 0 --> 3, level 1 --> 7 */ + SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, + SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, + SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, + SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, + SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, + SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, + SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, + SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, + + /* Onboard devices (not relevant/used on SunFire). */ + SYSIO_IMAP_SCSI, + SYSIO_IMAP_ETH, + SYSIO_IMAP_BPP, + bogon, + SYSIO_IMAP_AUDIO, + SYSIO_IMAP_PFAIL, + bogon, + bogon, + SYSIO_IMAP_KMS, + SYSIO_IMAP_FLPY, + SYSIO_IMAP_SHW, + SYSIO_IMAP_KBD, + SYSIO_IMAP_MS, + SYSIO_IMAP_SER, + bogon, + bogon, + SYSIO_IMAP_TIM0, + SYSIO_IMAP_TIM1, + bogon, + bogon, + SYSIO_IMAP_UE, + SYSIO_IMAP_CE, + SYSIO_IMAP_SBERR, + SYSIO_IMAP_PMGMT, +}; + +#undef bogon + +#define NUM_SYSIO_OFFSETS ARRAY_SIZE(sysio_irq_offsets) + +/* Convert Interrupt Mapping register pointer to associated + * Interrupt Clear register pointer, SYSIO specific version. + */ +#define SYSIO_ICLR_UNUSED0 0x3400UL +#define SYSIO_ICLR_SLOT0 0x340cUL +#define SYSIO_ICLR_SLOT1 0x344cUL +#define SYSIO_ICLR_SLOT2 0x348cUL +#define SYSIO_ICLR_SLOT3 0x34ccUL +static unsigned long sysio_imap_to_iclr(unsigned long imap) +{ + unsigned long diff = SYSIO_ICLR_UNUSED0 - SYSIO_IMAP_SLOT0; + return imap + diff; +} + +static unsigned int sbus_of_build_irq(struct device_node *dp, + unsigned int ino, + void *_data) +{ + unsigned long reg_base = (unsigned long) _data; + struct linux_prom_registers *regs; + unsigned long imap, iclr; + int sbus_slot = 0; + int sbus_level = 0; + + ino &= 0x3f; + + regs = of_get_property(dp, "reg", NULL); + if (regs) + sbus_slot = regs->which_io; + + if (ino < 0x20) + ino += (sbus_slot * 8); + + imap = sysio_irq_offsets[ino]; + if (imap == ((unsigned long)-1)) { + prom_printf("get_irq_translations: Bad SYSIO INO[%x]\n", + ino); + prom_halt(); + } + imap += reg_base; + + /* SYSIO inconsistency. For external SLOTS, we have to select + * the right ICLR register based upon the lower SBUS irq level + * bits. + */ + if (ino >= 0x20) { + iclr = sysio_imap_to_iclr(imap); + } else { + sbus_level = ino & 0x7; + + switch(sbus_slot) { + case 0: + iclr = reg_base + SYSIO_ICLR_SLOT0; + break; + case 1: + iclr = reg_base + SYSIO_ICLR_SLOT1; + break; + case 2: + iclr = reg_base + SYSIO_ICLR_SLOT2; + break; + default: + case 3: + iclr = reg_base + SYSIO_ICLR_SLOT3; + break; + }; + + iclr += ((unsigned long)sbus_level - 1UL) * 8UL; + } + return build_irq(sbus_level, iclr, imap); +} + +static void sbus_irq_trans_init(struct device_node *dp) +{ + struct linux_prom64_registers *regs; + + dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); + dp->irq_trans->irq_build = sbus_of_build_irq; + + regs = of_get_property(dp, "reg", NULL); + dp->irq_trans->data = (void *) (unsigned long) regs->phys_addr; +} +#endif /* CONFIG_SBUS */ + + +static unsigned int central_build_irq(struct device_node *dp, + unsigned int ino, + void *_data) +{ + struct device_node *central_dp = _data; + struct of_device *central_op = of_find_device_by_node(central_dp); + struct resource *res; + unsigned long imap, iclr; + u32 tmp; + + if (!strcmp(dp->name, "eeprom")) { + res = ¢ral_op->resource[5]; + } else if (!strcmp(dp->name, "zs")) { + res = ¢ral_op->resource[4]; + } else if (!strcmp(dp->name, "clock-board")) { + res = ¢ral_op->resource[3]; + } else { + return ino; + } + + imap = res->start + 0x00UL; + iclr = res->start + 0x10UL; + + /* Set the INO state to idle, and disable. */ + upa_writel(0, iclr); + upa_readl(iclr); + + tmp = upa_readl(imap); + tmp &= ~0x80000000; + upa_writel(tmp, imap); + + return build_irq(0, iclr, imap); +} + +static void central_irq_trans_init(struct device_node *dp) +{ + dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); + dp->irq_trans->irq_build = central_build_irq; + + dp->irq_trans->data = dp; +} + +struct irq_trans { + const char *name; + void (*init)(struct device_node *); +}; + +#ifdef CONFIG_PCI +static struct irq_trans pci_irq_trans_table[] = { + { "SUNW,sabre", sabre_irq_trans_init }, + { "pci108e,a000", sabre_irq_trans_init }, + { "pci108e,a001", sabre_irq_trans_init }, + { "SUNW,psycho", psycho_irq_trans_init }, + { "pci108e,8000", psycho_irq_trans_init }, + { "SUNW,schizo", schizo_irq_trans_init }, + { "pci108e,8001", schizo_irq_trans_init }, + { "SUNW,schizo+", schizo_irq_trans_init }, + { "pci108e,8002", schizo_irq_trans_init }, + { "SUNW,tomatillo", schizo_irq_trans_init }, + { "pci108e,a801", schizo_irq_trans_init }, + { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init }, +}; +#endif + +static unsigned int sun4v_vdev_irq_build(struct device_node *dp, + unsigned int devino, + void *_data) +{ + u32 devhandle = (u32) (unsigned long) _data; + + return sun4v_build_irq(devhandle, devino); +} + +static void sun4v_vdev_irq_trans_init(struct device_node *dp) +{ + struct linux_prom64_registers *regs; + + dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); + dp->irq_trans->irq_build = sun4v_vdev_irq_build; + + regs = of_get_property(dp, "reg", NULL); + dp->irq_trans->data = (void *) (unsigned long) + ((regs->phys_addr >> 32UL) & 0x0fffffff); +} + +static void irq_trans_init(struct device_node *dp) +{ + const char *model; + int i; + + model = of_get_property(dp, "model", NULL); + if (!model) + model = of_get_property(dp, "compatible", NULL); + if (!model) + return; + +#ifdef CONFIG_PCI + for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) { + struct irq_trans *t = &pci_irq_trans_table[i]; + + if (!strcmp(model, t->name)) + return t->init(dp); + } +#endif +#ifdef CONFIG_SBUS + if (!strcmp(dp->name, "sbus") || + !strcmp(dp->name, "sbi")) + return sbus_irq_trans_init(dp); +#endif + if (!strcmp(dp->name, "central")) + return central_irq_trans_init(dp->child); + if (!strcmp(dp->name, "virtual-devices")) + return sun4v_vdev_irq_trans_init(dp); +} + +static int is_root_node(const struct device_node *dp) +{ + if (!dp) + return 0; + + return (dp->parent == NULL); +} + +/* The following routines deal with the black magic of fully naming a + * node. + * + * Certain well known named nodes are just the simple name string. + * + * Actual devices have an address specifier appended to the base name + * string, like this "foo@addr". The "addr" can be in any number of + * formats, and the platform plus the type of the node determine the + * format and how it is constructed. + * + * For children of the ROOT node, the naming convention is fixed and + * determined by whether this is a sun4u or sun4v system. + * + * For children of other nodes, it is bus type specific. So + * we walk up the tree until we discover a "device_type" property + * we recognize and we go from there. + * + * As an example, the boot device on my workstation has a full path: + * + * /pci@1e,600000/ide@d/disk@0,0:c + */ +static void __init sun4v_path_component(struct device_node *dp, char *tmp_buf) +{ + struct linux_prom64_registers *regs; + struct property *rprop; + u32 high_bits, low_bits, type; + + rprop = of_find_property(dp, "reg", NULL); + if (!rprop) + return; + + regs = rprop->value; + if (!is_root_node(dp->parent)) { + sprintf(tmp_buf, "%s@%x,%x", + dp->name, + (unsigned int) (regs->phys_addr >> 32UL), + (unsigned int) (regs->phys_addr & 0xffffffffUL)); + return; + } + + type = regs->phys_addr >> 60UL; + high_bits = (regs->phys_addr >> 32UL) & 0x0fffffffUL; + low_bits = (regs->phys_addr & 0xffffffffUL); + + if (type == 0 || type == 8) { + const char *prefix = (type == 0) ? "m" : "i"; + + if (low_bits) + sprintf(tmp_buf, "%s@%s%x,%x", + dp->name, prefix, + high_bits, low_bits); + else + sprintf(tmp_buf, "%s@%s%x", + dp->name, + prefix, + high_bits); + } else if (type == 12) { + sprintf(tmp_buf, "%s@%x", + dp->name, high_bits); + } +} + +static void __init sun4u_path_component(struct device_node *dp, char *tmp_buf) +{ + struct linux_prom64_registers *regs; + struct property *prop; + + prop = of_find_property(dp, "reg", NULL); + if (!prop) + return; + + regs = prop->value; + if (!is_root_node(dp->parent)) { + sprintf(tmp_buf, "%s@%x,%x", + dp->name, + (unsigned int) (regs->phys_addr >> 32UL), + (unsigned int) (regs->phys_addr & 0xffffffffUL)); + return; + } + + prop = of_find_property(dp, "upa-portid", NULL); + if (!prop) + prop = of_find_property(dp, "portid", NULL); + if (prop) { + unsigned long mask = 0xffffffffUL; + + if (tlb_type >= cheetah) + mask = 0x7fffff; + + sprintf(tmp_buf, "%s@%x,%x", + dp->name, + *(u32 *)prop->value, + (unsigned int) (regs->phys_addr & mask)); + } +} + +/* "name@slot,offset" */ +static void __init sbus_path_component(struct device_node *dp, char *tmp_buf) +{ + struct linux_prom_registers *regs; + struct property *prop; + + prop = of_find_property(dp, "reg", NULL); + if (!prop) + return; + + regs = prop->value; + sprintf(tmp_buf, "%s@%x,%x", + dp->name, + regs->which_io, + regs->phys_addr); +} + +/* "name@devnum[,func]" */ +static void __init pci_path_component(struct device_node *dp, char *tmp_buf) +{ + struct linux_prom_pci_registers *regs; + struct property *prop; + unsigned int devfn; + + prop = of_find_property(dp, "reg", NULL); + if (!prop) + return; + + regs = prop->value; + devfn = (regs->phys_hi >> 8) & 0xff; + if (devfn & 0x07) { + sprintf(tmp_buf, "%s@%x,%x", + dp->name, + devfn >> 3, + devfn & 0x07); + } else { + sprintf(tmp_buf, "%s@%x", + dp->name, + devfn >> 3); + } +} + +/* "name@UPA_PORTID,offset" */ +static void __init upa_path_component(struct device_node *dp, char *tmp_buf) +{ + struct linux_prom64_registers *regs; + struct property *prop; + + prop = of_find_property(dp, "reg", NULL); + if (!prop) + return; + + regs = prop->value; + + prop = of_find_property(dp, "upa-portid", NULL); + if (!prop) + return; + + sprintf(tmp_buf, "%s@%x,%x", + dp->name, + *(u32 *) prop->value, + (unsigned int) (regs->phys_addr & 0xffffffffUL)); +} + +/* "name@reg" */ +static void __init vdev_path_component(struct device_node *dp, char *tmp_buf) +{ + struct property *prop; + u32 *regs; + + prop = of_find_property(dp, "reg", NULL); + if (!prop) + return; + + regs = prop->value; + + sprintf(tmp_buf, "%s@%x", dp->name, *regs); +} + +/* "name@addrhi,addrlo" */ +static void __init ebus_path_component(struct device_node *dp, char *tmp_buf) +{ + struct linux_prom64_registers *regs; + struct property *prop; + + prop = of_find_property(dp, "reg", NULL); + if (!prop) + return; + + regs = prop->value; + + sprintf(tmp_buf, "%s@%x,%x", + dp->name, + (unsigned int) (regs->phys_addr >> 32UL), + (unsigned int) (regs->phys_addr & 0xffffffffUL)); +} + +/* "name@bus,addr" */ +static void __init i2c_path_component(struct device_node *dp, char *tmp_buf) +{ + struct property *prop; + u32 *regs; + + prop = of_find_property(dp, "reg", NULL); + if (!prop) + return; + + regs = prop->value; + + /* This actually isn't right... should look at the #address-cells + * property of the i2c bus node etc. etc. + */ + sprintf(tmp_buf, "%s@%x,%x", + dp->name, regs[0], regs[1]); +} + +/* "name@reg0[,reg1]" */ +static void __init usb_path_component(struct device_node *dp, char *tmp_buf) +{ + struct property *prop; + u32 *regs; + + prop = of_find_property(dp, "reg", NULL); + if (!prop) + return; + + regs = prop->value; + + if (prop->length == sizeof(u32) || regs[1] == 1) { + sprintf(tmp_buf, "%s@%x", + dp->name, regs[0]); + } else { + sprintf(tmp_buf, "%s@%x,%x", + dp->name, regs[0], regs[1]); + } +} + +/* "name@reg0reg1[,reg2reg3]" */ +static void __init ieee1394_path_component(struct device_node *dp, char *tmp_buf) +{ + struct property *prop; + u32 *regs; + + prop = of_find_property(dp, "reg", NULL); + if (!prop) + return; + + regs = prop->value; + + if (regs[2] || regs[3]) { + sprintf(tmp_buf, "%s@%08x%08x,%04x%08x", + dp->name, regs[0], regs[1], regs[2], regs[3]); + } else { + sprintf(tmp_buf, "%s@%08x%08x", + dp->name, regs[0], regs[1]); + } +} + +static void __init __build_path_component(struct device_node *dp, char *tmp_buf) +{ + struct device_node *parent = dp->parent; + + if (parent != NULL) { + if (!strcmp(parent->type, "pci") || + !strcmp(parent->type, "pciex")) + return pci_path_component(dp, tmp_buf); + if (!strcmp(parent->type, "sbus")) + return sbus_path_component(dp, tmp_buf); + if (!strcmp(parent->type, "upa")) + return upa_path_component(dp, tmp_buf); + if (!strcmp(parent->type, "ebus")) + return ebus_path_component(dp, tmp_buf); + if (!strcmp(parent->name, "usb") || + !strcmp(parent->name, "hub")) + return usb_path_component(dp, tmp_buf); + if (!strcmp(parent->type, "i2c")) + return i2c_path_component(dp, tmp_buf); + if (!strcmp(parent->type, "firewire")) + return ieee1394_path_component(dp, tmp_buf); + if (!strcmp(parent->type, "virtual-devices")) + return vdev_path_component(dp, tmp_buf); + + /* "isa" is handled with platform naming */ + } + + /* Use platform naming convention. */ + if (tlb_type == hypervisor) + return sun4v_path_component(dp, tmp_buf); + else + return sun4u_path_component(dp, tmp_buf); +} + +static char * __init build_path_component(struct device_node *dp) +{ + char tmp_buf[64], *n; + + tmp_buf[0] = '\0'; + __build_path_component(dp, tmp_buf); + if (tmp_buf[0] == '\0') + strcpy(tmp_buf, dp->name); + + n = prom_early_alloc(strlen(tmp_buf) + 1); + strcpy(n, tmp_buf); + + return n; +} + +static char * __init build_full_name(struct device_node *dp) +{ + int len, ourlen, plen; + char *n; + + plen = strlen(dp->parent->full_name); + ourlen = strlen(dp->path_component_name); + len = ourlen + plen + 2; + + n = prom_early_alloc(len); + strcpy(n, dp->parent->full_name); + if (!is_root_node(dp->parent)) { + strcpy(n + plen, "/"); + plen++; + } + strcpy(n + plen, dp->path_component_name); + + return n; +} + +static unsigned int unique_id; + +static struct property * __init build_one_prop(phandle node, char *prev, char *special_name, void *special_val, int special_len) +{ + static struct property *tmp = NULL; + struct property *p; + + if (tmp) { + p = tmp; + memset(p, 0, sizeof(*p) + 32); + tmp = NULL; + } else { + p = prom_early_alloc(sizeof(struct property) + 32); + p->unique_id = unique_id++; + } + + p->name = (char *) (p + 1); + if (special_name) { + strcpy(p->name, special_name); + p->length = special_len; + p->value = prom_early_alloc(special_len); + memcpy(p->value, special_val, special_len); + } else { + if (prev == NULL) { + prom_firstprop(node, p->name); + } else { + prom_nextprop(node, prev, p->name); + } + if (strlen(p->name) == 0) { + tmp = p; + return NULL; + } + p->length = prom_getproplen(node, p->name); + if (p->length <= 0) { + p->length = 0; + } else { + p->value = prom_early_alloc(p->length + 1); + prom_getproperty(node, p->name, p->value, p->length); + ((unsigned char *)p->value)[p->length] = '\0'; + } + } + return p; +} + +static struct property * __init build_prop_list(phandle node) +{ + struct property *head, *tail; + + head = tail = build_one_prop(node, NULL, + ".node", &node, sizeof(node)); + + tail->next = build_one_prop(node, NULL, NULL, NULL, 0); + tail = tail->next; + while(tail) { + tail->next = build_one_prop(node, tail->name, + NULL, NULL, 0); + tail = tail->next; + } + + return head; +} + +static char * __init get_one_property(phandle node, const char *name) +{ + char *buf = "<NULL>"; + int len; + + len = prom_getproplen(node, name); + if (len > 0) { + buf = prom_early_alloc(len); + prom_getproperty(node, name, buf, len); + } + + return buf; +} + +static struct device_node * __init create_node(phandle node) +{ + struct device_node *dp; + + if (!node) + return NULL; + + dp = prom_early_alloc(sizeof(*dp)); + dp->unique_id = unique_id++; + + kref_init(&dp->kref); + + dp->name = get_one_property(node, "name"); + dp->type = get_one_property(node, "device_type"); + dp->node = node; + + dp->properties = build_prop_list(node); + + irq_trans_init(dp); + + return dp; +} + +static struct device_node * __init build_tree(struct device_node *parent, phandle node, struct device_node ***nextp) +{ + struct device_node *dp; + + dp = create_node(node); + if (dp) { + *(*nextp) = dp; + *nextp = &dp->allnext; + + dp->parent = parent; + dp->path_component_name = build_path_component(dp); + dp->full_name = build_full_name(dp); + + dp->child = build_tree(dp, prom_getchild(node), nextp); + + dp->sibling = build_tree(parent, prom_getsibling(node), nextp); + } + + return dp; +} + +void __init prom_build_devicetree(void) +{ + struct device_node **nextp; + + allnodes = create_node(prom_root_node); + allnodes->path_component_name = ""; + allnodes->full_name = "/"; + + nextp = &allnodes->allnext; + allnodes->child = build_tree(allnodes, + prom_getchild(allnodes->node), + &nextp); + printk("PROM: Built device tree with %u bytes of memory.\n", + prom_early_allocated); +} diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S index 7130e866f93..3522cd66f3b 100644 --- a/arch/sparc64/kernel/rtrap.S +++ b/arch/sparc64/kernel/rtrap.S @@ -5,7 +5,6 @@ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) */ -#include <linux/config.h> #include <asm/asi.h> #include <asm/pstate.h> diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc64/kernel/sbus.c index 1d6ffdeabd4..c49a5779574 100644 --- a/arch/sparc64/kernel/sbus.c +++ b/arch/sparc64/kernel/sbus.c @@ -19,6 +19,7 @@ #include <asm/cache.h> #include <asm/dma.h> #include <asm/irq.h> +#include <asm/prom.h> #include <asm/starfire.h> #include "iommu_common.h" @@ -691,36 +692,6 @@ void sbus_set_sbus64(struct sbus_dev *sdev, int bursts) upa_writeq(val, cfg_reg); } -/* SBUS SYSIO INO number to Sparc PIL level. */ -static unsigned char sysio_ino_to_pil[] = { - 0, 5, 5, 7, 5, 7, 8, 9, /* SBUS slot 0 */ - 0, 5, 5, 7, 5, 7, 8, 9, /* SBUS slot 1 */ - 0, 5, 5, 7, 5, 7, 8, 9, /* SBUS slot 2 */ - 0, 5, 5, 7, 5, 7, 8, 9, /* SBUS slot 3 */ - 5, /* Onboard SCSI */ - 5, /* Onboard Ethernet */ -/*XXX*/ 8, /* Onboard BPP */ - 0, /* Bogon */ - 13, /* Audio */ -/*XXX*/15, /* PowerFail */ - 0, /* Bogon */ - 0, /* Bogon */ - 12, /* Zilog Serial Channels (incl. Keyboard/Mouse lines) */ - 11, /* Floppy */ - 0, /* Spare Hardware (bogon for now) */ - 0, /* Keyboard (bogon for now) */ - 0, /* Mouse (bogon for now) */ - 0, /* Serial (bogon for now) */ - 0, 0, /* Bogon, Bogon */ - 10, /* Timer 0 */ - 11, /* Timer 1 */ - 0, 0, /* Bogon, Bogon */ - 15, /* Uncorrectable SBUS Error */ - 15, /* Correctable SBUS Error */ - 15, /* SBUS Error */ -/*XXX*/ 0, /* Power Management (bogon for now) */ -}; - /* INO number to IMAP register offset for SYSIO external IRQ's. * This should conform to both Sunfire/Wildfire server and Fusion * desktop designs. @@ -812,21 +783,12 @@ unsigned int sbus_build_irq(void *buscookie, unsigned int ino) struct sbus_iommu *iommu = sbus->iommu; unsigned long reg_base = iommu->sbus_control_reg - 0x2000UL; unsigned long imap, iclr; - int pil, sbus_level = 0; - - pil = sysio_ino_to_pil[ino]; - if (!pil) { - printk("sbus_irq_build: Bad SYSIO INO[%x]\n", ino); - panic("Bad SYSIO IRQ translations..."); - } - - if (PIL_RESERVED(pil)) - BUG(); + int sbus_level = 0; imap = sysio_irq_offsets[ino]; if (imap == ((unsigned long)-1)) { - prom_printf("get_irq_translations: Bad SYSIO INO[%x] cpu[%d]\n", - ino, pil); + prom_printf("get_irq_translations: Bad SYSIO INO[%x]\n", + ino); prom_halt(); } imap += reg_base; @@ -860,7 +822,7 @@ unsigned int sbus_build_irq(void *buscookie, unsigned int ino) iclr += ((unsigned long)sbus_level - 1UL) * 8UL; } - return build_irq(pil, sbus_level, iclr, imap); + return build_irq(sbus_level, iclr, imap); } /* Error interrupt handling. */ @@ -1103,7 +1065,7 @@ static void __init sysio_register_error_handlers(struct sbus_bus *sbus) irq = sbus_build_irq(sbus, SYSIO_UE_INO); if (request_irq(irq, sysio_ue_handler, - SA_SHIRQ, "SYSIO UE", sbus) < 0) { + IRQF_SHARED, "SYSIO UE", sbus) < 0) { prom_printf("SYSIO[%x]: Cannot register UE interrupt.\n", sbus->portid); prom_halt(); @@ -1111,7 +1073,7 @@ static void __init sysio_register_error_handlers(struct sbus_bus *sbus) irq = sbus_build_irq(sbus, SYSIO_CE_INO); if (request_irq(irq, sysio_ce_handler, - SA_SHIRQ, "SYSIO CE", sbus) < 0) { + IRQF_SHARED, "SYSIO CE", sbus) < 0) { prom_printf("SYSIO[%x]: Cannot register CE interrupt.\n", sbus->portid); prom_halt(); @@ -1119,7 +1081,7 @@ static void __init sysio_register_error_handlers(struct sbus_bus *sbus) irq = sbus_build_irq(sbus, SYSIO_SBUSERR_INO); if (request_irq(irq, sysio_sbus_error_handler, - SA_SHIRQ, "SYSIO SBUS Error", sbus) < 0) { + IRQF_SHARED, "SYSIO SBUS Error", sbus) < 0) { prom_printf("SYSIO[%x]: Cannot register SBUS Error interrupt.\n", sbus->portid); prom_halt(); @@ -1137,24 +1099,25 @@ static void __init sysio_register_error_handlers(struct sbus_bus *sbus) } /* Boot time initialization. */ -void __init sbus_iommu_init(int prom_node, struct sbus_bus *sbus) +static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) { - struct linux_prom64_registers rprop; + struct linux_prom64_registers *pr; + struct device_node *dp; struct sbus_iommu *iommu; unsigned long regs, tsb_base; u64 control; - int err, i; + int i; - sbus->portid = prom_getintdefault(sbus->prom_node, - "upa-portid", -1); + dp = of_find_node_by_phandle(__node); - err = prom_getproperty(prom_node, "reg", - (char *)&rprop, sizeof(rprop)); - if (err < 0) { + sbus->portid = of_getintprop_default(dp, "upa-portid", -1); + + pr = of_get_property(dp, "reg", NULL); + if (!pr) { prom_printf("sbus_iommu_init: Cannot map SYSIO control registers.\n"); prom_halt(); } - regs = rprop.phys_addr; + regs = pr->phys_addr; iommu = kmalloc(sizeof(*iommu) + SMP_CACHE_BYTES, GFP_ATOMIC); if (iommu == NULL) { @@ -1258,9 +1221,52 @@ void __init sbus_iommu_init(int prom_node, struct sbus_bus *sbus) /* Now some Xfire specific grot... */ if (this_is_starfire) - sbus->starfire_cookie = starfire_hookup(sbus->portid); - else - sbus->starfire_cookie = NULL; + starfire_hookup(sbus->portid); sysio_register_error_handlers(sbus); } + +void sbus_fill_device_irq(struct sbus_dev *sdev) +{ + struct device_node *dp = of_find_node_by_phandle(sdev->prom_node); + struct linux_prom_irqs *irqs; + + irqs = of_get_property(dp, "interrupts", NULL); + if (!irqs) { + sdev->irqs[0] = 0; + sdev->num_irqs = 0; + } else { + unsigned int pri = irqs[0].pri; + + sdev->num_irqs = 1; + if (pri < 0x20) + pri += sdev->slot * 8; + + sdev->irqs[0] = sbus_build_irq(sdev->bus, pri); + } +} + +void __init sbus_arch_bus_ranges_init(struct device_node *pn, struct sbus_bus *sbus) +{ +} + +void __init sbus_setup_iommu(struct sbus_bus *sbus, struct device_node *dp) +{ + sbus_iommu_init(dp->node, sbus); +} + +void __init sbus_setup_arch_props(struct sbus_bus *sbus, struct device_node *dp) +{ +} + +int __init sbus_arch_preinit(void) +{ + return 0; +} + +void __init sbus_arch_postinit(void) +{ + extern void firetruck_init(void); + + firetruck_init(); +} diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 9cf1c88cd77..a73140466e0 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -18,7 +18,6 @@ #include <linux/a.out.h> #include <linux/tty.h> #include <linux/delay.h> -#include <linux/config.h> #include <linux/fs.h> #include <linux/seq_file.h> #include <linux/syscalls.h> @@ -376,12 +375,12 @@ void __init setup_arch(char **cmdline_p) } #endif - smp_setup_cpu_possible_map(); - /* Get boot processor trap_block[] setup. */ init_cur_cpu_trap(current_thread_info()); paging_init(); + + smp_setup_cpu_possible_map(); } static int __init set_preferred_console(void) @@ -537,7 +536,7 @@ static int __init topology_init(void) for_each_possible_cpu(i) { struct cpu *p = kzalloc(sizeof(*p), GFP_KERNEL); if (p) { - register_cpu(p, i, NULL); + register_cpu(p, i); err = 0; } } diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index ca11a4c457d..96d56a8410a 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -8,7 +8,6 @@ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include <linux/config.h> #ifdef CONFIG_SPARC32_COMPAT #include <linux/compat.h> /* for compat_old_sigset_t */ #endif diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index f03d52d0b88..f62bf3a2de1 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -39,6 +39,7 @@ #include <asm/starfire.h> #include <asm/tlb.h> #include <asm/sections.h> +#include <asm/prom.h> extern void calibrate_delay(void); @@ -76,41 +77,42 @@ void smp_bogo(struct seq_file *m) void __init smp_store_cpu_info(int id) { - int cpu_node, def; + struct device_node *dp; + int def; /* multiplier and counter set by smp_setup_percpu_timer() */ cpu_data(id).udelay_val = loops_per_jiffy; - cpu_find_by_mid(id, &cpu_node); - cpu_data(id).clock_tick = prom_getintdefault(cpu_node, - "clock-frequency", 0); + cpu_find_by_mid(id, &dp); + cpu_data(id).clock_tick = + of_getintprop_default(dp, "clock-frequency", 0); def = ((tlb_type == hypervisor) ? (8 * 1024) : (16 * 1024)); - cpu_data(id).dcache_size = prom_getintdefault(cpu_node, "dcache-size", - def); + cpu_data(id).dcache_size = + of_getintprop_default(dp, "dcache-size", def); def = 32; cpu_data(id).dcache_line_size = - prom_getintdefault(cpu_node, "dcache-line-size", def); + of_getintprop_default(dp, "dcache-line-size", def); def = 16 * 1024; - cpu_data(id).icache_size = prom_getintdefault(cpu_node, "icache-size", - def); + cpu_data(id).icache_size = + of_getintprop_default(dp, "icache-size", def); def = 32; cpu_data(id).icache_line_size = - prom_getintdefault(cpu_node, "icache-line-size", def); + of_getintprop_default(dp, "icache-line-size", def); def = ((tlb_type == hypervisor) ? (3 * 1024 * 1024) : (4 * 1024 * 1024)); - cpu_data(id).ecache_size = prom_getintdefault(cpu_node, "ecache-size", - def); + cpu_data(id).ecache_size = + of_getintprop_default(dp, "ecache-size", def); def = 64; cpu_data(id).ecache_line_size = - prom_getintdefault(cpu_node, "ecache-line-size", def); + of_getintprop_default(dp, "ecache-line-size", def); printk("CPU[%d]: Caches " "D[sz(%d):line_sz(%d)] " @@ -342,10 +344,10 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu) prom_startcpu_cpuid(cpu, entry, cookie); } else { - int cpu_node; + struct device_node *dp; - cpu_find_by_mid(cpu, &cpu_node); - prom_startcpu(cpu_node, entry, cookie); + cpu_find_by_mid(cpu, &dp); + prom_startcpu(dp->node, entry, cookie); } for (timeout = 0; timeout < 5000000; timeout++) { @@ -1289,7 +1291,8 @@ int setup_profiling_timer(unsigned int multiplier) static void __init smp_tune_scheduling(void) { - int instance, node; + struct device_node *dp; + int instance; unsigned int def, smallest = ~0U; def = ((tlb_type == hypervisor) ? @@ -1297,10 +1300,10 @@ static void __init smp_tune_scheduling(void) (4 * 1024 * 1024)); instance = 0; - while (!cpu_find_by_instance(instance, &node, NULL)) { + while (!cpu_find_by_instance(instance, &dp, NULL)) { unsigned int val; - val = prom_getintdefault(node, "ecache-size", def); + val = of_getintprop_default(dp, "ecache-size", def); if (val < smallest) smallest = val; diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 38e569f786d..4173de425f0 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -10,7 +10,6 @@ #define EXPORT_SYMTAB_STROPS #define PROMLIB_INTERNAL -#include <linux/config.h> #include <linux/module.h> #include <linux/types.h> #include <linux/string.h> @@ -125,9 +124,6 @@ EXPORT_SYMBOL(__write_lock); EXPORT_SYMBOL(__write_unlock); EXPORT_SYMBOL(__write_trylock); -/* Hard IRQ locking */ -EXPORT_SYMBOL(synchronize_irq); - #if defined(CONFIG_MCOUNT) extern void _mcount(void); EXPORT_SYMBOL(_mcount); @@ -175,10 +171,6 @@ EXPORT_SYMBOL(set_bit); EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(change_bit); -EXPORT_SYMBOL(ivector_table); -EXPORT_SYMBOL(enable_irq); -EXPORT_SYMBOL(disable_irq); - EXPORT_SYMBOL(__flushw_user); EXPORT_SYMBOL(tlb_type); diff --git a/arch/sparc64/kernel/starfire.c b/arch/sparc64/kernel/starfire.c index ae859d40771..b930fee7708 100644 --- a/arch/sparc64/kernel/starfire.c +++ b/arch/sparc64/kernel/starfire.c @@ -54,7 +54,7 @@ struct starfire_irqinfo { static struct starfire_irqinfo *sflist = NULL; /* Beam me up Scott(McNeil)y... */ -void *starfire_hookup(int upaid) +void starfire_hookup(int upaid) { struct starfire_irqinfo *p; unsigned long treg_base, hwmid, i; @@ -81,8 +81,6 @@ void *starfire_hookup(int upaid) p->upaid = upaid; p->next = sflist; sflist = p; - - return (void *) p; } unsigned int starfire_translate(unsigned long imap, diff --git a/arch/sparc64/kernel/sun4v_ivec.S b/arch/sparc64/kernel/sun4v_ivec.S index b49a68bdda4..49703c3c576 100644 --- a/arch/sparc64/kernel/sun4v_ivec.S +++ b/arch/sparc64/kernel/sun4v_ivec.S @@ -5,6 +5,7 @@ #include <asm/cpudata.h> #include <asm/intr_queue.h> +#include <asm/pil.h> .text .align 32 @@ -102,23 +103,17 @@ sun4v_dev_mondo: /* Get &ivector_table[IVEC] into %g4. */ sethi %hi(ivector_table), %g4 - sllx %g3, 5, %g3 + sllx %g3, 3, %g3 or %g4, %lo(ivector_table), %g4 add %g4, %g3, %g4 - /* Load IRQ %pil into %g5. */ - ldub [%g4 + 0x04], %g5 - /* Insert ivector_table[] entry into __irq_work[] queue. */ - sllx %g5, 2, %g3 - lduw [%g1 + %g3], %g2 /* g2 = irq_work(cpu, pil) */ + lduw [%g1], %g2 /* g2 = irq_work(cpu) */ stw %g2, [%g4 + 0x00] /* bucket->irq_chain = g2 */ - stw %g4, [%g1 + %g3] /* irq_work(cpu, pil) = bucket */ + stw %g4, [%g1] /* irq_work(cpu) = bucket */ /* Signal the interrupt by setting (1 << pil) in %softint. */ - mov 1, %g2 - sllx %g2, %g5, %g2 - wr %g2, 0x0, %set_softint + wr %g0, 1 << PIL_DEVICE_IRQ, %set_softint sun4v_dev_mondo_queue_empty: retry diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc64/kernel/sys32.S index bdf1f4d02e3..c09ab4b9431 100644 --- a/arch/sparc64/kernel/sys32.S +++ b/arch/sparc64/kernel/sys32.S @@ -6,7 +6,6 @@ * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) */ -#include <linux/config.h> #include <asm/errno.h> /* NOTE: call as jump breaks return stack, we have to avoid that */ diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 7a869138c37..51c056df528 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -6,7 +6,6 @@ * platform. */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/sched.h> diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 31030bf00f1..c88ae23ce81 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -8,7 +8,6 @@ * environment. */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/capability.h> diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index ae5b32f817f..87ebdf858a3 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -155,7 +155,7 @@ asmlinkage int sunos_brk(u32 baddr) * simple, it hopefully works in most obvious cases.. Easy to * fool it, but this should catch most mistakes. */ - freepages = get_page_cache_size(); + freepages = global_page_state(NR_FILE_PAGES); freepages >>= 1; freepages += nr_free_pages(); freepages += nr_swap_pages; diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 1136fc465e3..419a63fca17 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -10,7 +10,6 @@ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu) */ -#include <linux/config.h> .text .align 4 diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index e55b5c6ece0..8dcbfbffacc 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -9,7 +9,6 @@ * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/sched.h> @@ -38,16 +37,14 @@ #include <asm/timer.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/sbus.h> -#include <asm/fhc.h> -#include <asm/pbm.h> -#include <asm/ebus.h> -#include <asm/isa.h> +#include <asm/prom.h> +#include <asm/of_device.h> #include <asm/starfire.h> #include <asm/smp.h> #include <asm/sections.h> #include <asm/cpudata.h> #include <asm/uaccess.h> +#include <asm/prom.h> DEFINE_SPINLOCK(mostek_lock); DEFINE_SPINLOCK(rtc_lock); @@ -457,7 +454,7 @@ static inline void timer_check_rtc(void) } } -static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) { unsigned long ticks, compare, pstate; @@ -755,215 +752,60 @@ retry: return -EOPNOTSUPP; } -void __init clock_probe(void) +static int __init clock_model_matches(char *model) { - struct linux_prom_registers clk_reg[2]; - char model[128]; - int node, busnd = -1, err; - unsigned long flags; - struct linux_central *cbus; -#ifdef CONFIG_PCI - struct linux_ebus *ebus = NULL; - struct sparc_isa_bridge *isa_br = NULL; -#endif - static int invoked; - - if (invoked) - return; - invoked = 1; - + if (strcmp(model, "mk48t02") && + strcmp(model, "mk48t08") && + strcmp(model, "mk48t59") && + strcmp(model, "m5819") && + strcmp(model, "m5819p") && + strcmp(model, "m5823") && + strcmp(model, "ds1287")) + return 0; - if (this_is_starfire) { - xtime.tv_sec = starfire_get_time(); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - return; - } - if (tlb_type == hypervisor) { - xtime.tv_sec = hypervisor_get_time(); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - return; - } + return 1; +} - local_irq_save(flags); +static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) +{ + struct device_node *dp = op->node; + char *model = of_get_property(dp, "model", NULL); + unsigned long size, flags; + void __iomem *regs; - cbus = central_bus; - if (cbus != NULL) - busnd = central_bus->child->prom_node; + if (!model || !clock_model_matches(model)) + return -ENODEV; - /* Check FHC Central then EBUSs then ISA bridges then SBUSs. - * That way we handle the presence of multiple properly. - * - * As a special case, machines with Central must provide the - * timer chip there. + /* On an Enterprise system there can be multiple mostek clocks. + * We should only match the one that is on the central FHC bus. */ -#ifdef CONFIG_PCI - if (ebus_chain != NULL) { - ebus = ebus_chain; - if (busnd == -1) - busnd = ebus->prom_node; - } - if (isa_chain != NULL) { - isa_br = isa_chain; - if (busnd == -1) - busnd = isa_br->prom_node; - } -#endif - if (sbus_root != NULL && busnd == -1) - busnd = sbus_root->prom_node; + if (!strcmp(dp->parent->name, "fhc") && + strcmp(dp->parent->parent->name, "central") != 0) + return -ENODEV; - if (busnd == -1) { - prom_printf("clock_probe: problem, cannot find bus to search.\n"); - prom_halt(); + size = (op->resource[0].end - op->resource[0].start) + 1; + regs = of_ioremap(&op->resource[0], 0, size, "clock"); + if (!regs) + return -ENOMEM; + + if (!strcmp(model, "ds1287") || + !strcmp(model, "m5819") || + !strcmp(model, "m5819p") || + !strcmp(model, "m5823")) { + ds1287_regs = (unsigned long) regs; + } else if (model[5] == '0' && model[6] == '2') { + mstk48t02_regs = regs; + } else if(model[5] == '0' && model[6] == '8') { + mstk48t08_regs = regs; + mstk48t02_regs = mstk48t08_regs + MOSTEK_48T08_48T02; + } else { + mstk48t59_regs = regs; + mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; } - node = prom_getchild(busnd); - - while (1) { - if (!node) - model[0] = 0; - else - prom_getstring(node, "model", model, sizeof(model)); - if (strcmp(model, "mk48t02") && - strcmp(model, "mk48t08") && - strcmp(model, "mk48t59") && - strcmp(model, "m5819") && - strcmp(model, "m5819p") && - strcmp(model, "m5823") && - strcmp(model, "ds1287")) { - if (cbus != NULL) { - prom_printf("clock_probe: Central bus lacks timer chip.\n"); - prom_halt(); - } - - if (node != 0) - node = prom_getsibling(node); -#ifdef CONFIG_PCI - while ((node == 0) && ebus != NULL) { - ebus = ebus->next; - if (ebus != NULL) { - busnd = ebus->prom_node; - node = prom_getchild(busnd); - } - } - while ((node == 0) && isa_br != NULL) { - isa_br = isa_br->next; - if (isa_br != NULL) { - busnd = isa_br->prom_node; - node = prom_getchild(busnd); - } - } -#endif - if (node == 0) { - prom_printf("clock_probe: Cannot find timer chip\n"); - prom_halt(); - } - continue; - } - - err = prom_getproperty(node, "reg", (char *)clk_reg, - sizeof(clk_reg)); - if(err == -1) { - prom_printf("clock_probe: Cannot get Mostek reg property\n"); - prom_halt(); - } - - if (cbus != NULL) { - apply_fhc_ranges(central_bus->child, clk_reg, 1); - apply_central_ranges(central_bus, clk_reg, 1); - } -#ifdef CONFIG_PCI - else if (ebus != NULL) { - struct linux_ebus_device *edev; - - for_each_ebusdev(edev, ebus) - if (edev->prom_node == node) - break; - if (edev == NULL) { - if (isa_chain != NULL) - goto try_isa_clock; - prom_printf("%s: Mostek not probed by EBUS\n", - __FUNCTION__); - prom_halt(); - } - - if (!strcmp(model, "ds1287") || - !strcmp(model, "m5819") || - !strcmp(model, "m5819p") || - !strcmp(model, "m5823")) { - ds1287_regs = edev->resource[0].start; - } else { - mstk48t59_regs = (void __iomem *) - edev->resource[0].start; - mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; - } - break; - } - else if (isa_br != NULL) { - struct sparc_isa_device *isadev; - -try_isa_clock: - for_each_isadev(isadev, isa_br) - if (isadev->prom_node == node) - break; - if (isadev == NULL) { - prom_printf("%s: Mostek not probed by ISA\n"); - prom_halt(); - } - if (!strcmp(model, "ds1287") || - !strcmp(model, "m5819") || - !strcmp(model, "m5819p") || - !strcmp(model, "m5823")) { - ds1287_regs = isadev->resource.start; - } else { - mstk48t59_regs = (void __iomem *) - isadev->resource.start; - mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; - } - break; - } -#endif - else { - if (sbus_root->num_sbus_ranges) { - int nranges = sbus_root->num_sbus_ranges; - int rngc; - - for (rngc = 0; rngc < nranges; rngc++) - if (clk_reg[0].which_io == - sbus_root->sbus_ranges[rngc].ot_child_space) - break; - if (rngc == nranges) { - prom_printf("clock_probe: Cannot find ranges for " - "clock regs.\n"); - prom_halt(); - } - clk_reg[0].which_io = - sbus_root->sbus_ranges[rngc].ot_parent_space; - clk_reg[0].phys_addr += - sbus_root->sbus_ranges[rngc].ot_parent_base; - } - } + printk(KERN_INFO "%s: Clock regs at %p\n", dp->full_name, regs); - if(model[5] == '0' && model[6] == '2') { - mstk48t02_regs = (void __iomem *) - (((u64)clk_reg[0].phys_addr) | - (((u64)clk_reg[0].which_io)<<32UL)); - } else if(model[5] == '0' && model[6] == '8') { - mstk48t08_regs = (void __iomem *) - (((u64)clk_reg[0].phys_addr) | - (((u64)clk_reg[0].which_io)<<32UL)); - mstk48t02_regs = mstk48t08_regs + MOSTEK_48T08_48T02; - } else { - mstk48t59_regs = (void __iomem *) - (((u64)clk_reg[0].phys_addr) | - (((u64)clk_reg[0].which_io)<<32UL)); - mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; - } - break; - } + local_irq_save(flags); if (mstk48t02_regs != NULL) { /* Report a low battery voltage condition. */ @@ -978,17 +820,63 @@ try_isa_clock: set_system_time(); local_irq_restore(flags); + + return 0; } +static struct of_device_id clock_match[] = { + { + .name = "eeprom", + }, + { + .name = "rtc", + }, + {}, +}; + +static struct of_platform_driver clock_driver = { + .name = "clock", + .match_table = clock_match, + .probe = clock_probe, +}; + +static int __init clock_init(void) +{ + if (this_is_starfire) { + xtime.tv_sec = starfire_get_time(); + xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); + return 0; + } + if (tlb_type == hypervisor) { + xtime.tv_sec = hypervisor_get_time(); + xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); + return 0; + } + + return of_register_driver(&clock_driver, &of_bus_type); +} + +/* Must be after subsys_initcall() so that busses are probed. Must + * be before device_initcall() because things like the RTC driver + * need to see the clock registers. + */ +fs_initcall(clock_init); + /* This is gets the master TICK_INT timer going. */ static unsigned long sparc64_init_timers(void) { + struct device_node *dp; + struct property *prop; unsigned long clock; - int node; #ifdef CONFIG_SMP extern void smp_tick_init(void); #endif + dp = of_find_node_by_path("/"); if (tlb_type == spitfire) { unsigned long ver, manuf, impl; @@ -999,18 +887,17 @@ static unsigned long sparc64_init_timers(void) if (manuf == 0x17 && impl == 0x13) { /* Hummingbird, aka Ultra-IIe */ tick_ops = &hbtick_operations; - node = prom_root_node; - clock = prom_getint(node, "stick-frequency"); + prop = of_find_property(dp, "stick-frequency", NULL); } else { tick_ops = &tick_operations; - cpu_find_by_instance(0, &node, NULL); - clock = prom_getint(node, "clock-frequency"); + cpu_find_by_instance(0, &dp, NULL); + prop = of_find_property(dp, "clock-frequency", NULL); } } else { tick_ops = &stick_operations; - node = prom_root_node; - clock = prom_getint(node, "stick-frequency"); + prop = of_find_property(dp, "stick-frequency", NULL); } + clock = *(unsigned int *) prop->value; timer_tick_offset = clock / HZ; #ifdef CONFIG_SMP @@ -1020,19 +907,9 @@ static unsigned long sparc64_init_timers(void) return clock; } -static void sparc64_start_timers(irqreturn_t (*cfunc)(int, void *, struct pt_regs *)) +static void sparc64_start_timers(void) { unsigned long pstate; - int err; - - /* Register IRQ handler. */ - err = request_irq(build_irq(0, 0, 0UL, 0UL), cfunc, 0, - "timer", NULL); - - if (err) { - prom_printf("Serious problem, cannot register TICK_INT\n"); - prom_halt(); - } /* Guarantee that the following sequences execute * uninterrupted. @@ -1116,7 +993,7 @@ void __init time_init(void) /* Now that the interpolator is registered, it is * safe to start the timer ticking. */ - sparc64_start_timers(timer_interrupt); + sparc64_start_timers(); timer_ticks_per_nsec_quotient = (((NSEC_PER_SEC << SPARC64_NSEC_PER_CYC_SHIFT) + diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 563db528e03..68420e2dad0 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -9,7 +9,6 @@ * I like traps on v9, :)))) */ -#include <linux/config.h> #include <linux/module.h> #include <linux/sched.h> /* for jiffies */ #include <linux/kernel.h> @@ -42,6 +41,7 @@ #ifdef CONFIG_KMOD #include <linux/kmod.h> #endif +#include <asm/prom.h> ATOMIC_NOTIFIER_HEAD(sparc64die_chain); @@ -807,7 +807,8 @@ extern unsigned int cheetah_deferred_trap_vector[], cheetah_deferred_trap_vector void __init cheetah_ecache_flush_init(void) { unsigned long largest_size, smallest_linesize, order, ver; - int node, i, instance; + struct device_node *dp; + int i, instance, sz; /* Scan all cpu device tree nodes, note two values: * 1) largest E-cache size @@ -817,14 +818,14 @@ void __init cheetah_ecache_flush_init(void) smallest_linesize = ~0UL; instance = 0; - while (!cpu_find_by_instance(instance, &node, NULL)) { + while (!cpu_find_by_instance(instance, &dp, NULL)) { unsigned long val; - val = prom_getintdefault(node, "ecache-size", - (2 * 1024 * 1024)); + val = of_getintprop_default(dp, "ecache-size", + (2 * 1024 * 1024)); if (val > largest_size) largest_size = val; - val = prom_getintdefault(node, "ecache-line-size", 64); + val = of_getintprop_default(dp, "ecache-line-size", 64); if (val < smallest_linesize) smallest_linesize = val; instance++; @@ -849,16 +850,16 @@ void __init cheetah_ecache_flush_init(void) } /* Now allocate error trap reporting scoreboard. */ - node = NR_CPUS * (2 * sizeof(struct cheetah_err_info)); + sz = NR_CPUS * (2 * sizeof(struct cheetah_err_info)); for (order = 0; order < MAX_ORDER; order++) { - if ((PAGE_SIZE << order) >= node) + if ((PAGE_SIZE << order) >= sz) break; } cheetah_error_log = (struct cheetah_err_info *) __get_free_pages(GFP_KERNEL, order); if (!cheetah_error_log) { prom_printf("cheetah_ecache_flush_init: Failed to allocate " - "error logging scoreboard (%d bytes).\n", node); + "error logging scoreboard (%d bytes).\n", sz); prom_halt(); } memset(cheetah_error_log, 0, PAGE_SIZE << order); @@ -2544,7 +2545,9 @@ void __init trap_init(void) (TRAP_PER_CPU_TSB_HUGE != offsetof(struct trap_per_cpu, tsb_huge)) || (TRAP_PER_CPU_TSB_HUGE_TEMP != - offsetof(struct trap_per_cpu, tsb_huge_temp))) + offsetof(struct trap_per_cpu, tsb_huge_temp)) || + (TRAP_PER_CPU_IRQ_WORKLIST != + offsetof(struct trap_per_cpu, irq_worklist))) trap_per_cpu_offsets_are_bolixed_dave(); if ((TSB_CONFIG_TSB != diff --git a/arch/sparc64/kernel/tsb.S b/arch/sparc64/kernel/tsb.S index a0c8ba58920..eedf94fa566 100644 --- a/arch/sparc64/kernel/tsb.S +++ b/arch/sparc64/kernel/tsb.S @@ -3,7 +3,6 @@ * Copyright (C) 2006 David S. Miller <davem@davemloft.net> */ -#include <linux/config.h> #include <asm/tsb.h> #include <asm/hypervisor.h> diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index 5d901519db5..d7d2a8bdc66 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -3,7 +3,6 @@ * Copyright (C) 1996, 2001, 2006 David S. Miller (davem@davemloft.net) */ -#include <linux/config.h> .globl sparc64_ttable_tl0, sparc64_ttable_tl1 .globl tl0_icpe, tl1_icpe @@ -58,13 +57,11 @@ tl0_irq2: BTRAP(0x42) tl0_irq3: BTRAP(0x43) tl0_irq4: BTRAP(0x44) #endif -tl0_irq5: TRAP_IRQ(handler_irq, 5) TRAP_IRQ(handler_irq, 6) -tl0_irq7: TRAP_IRQ(handler_irq, 7) TRAP_IRQ(handler_irq, 8) -tl0_irq9: TRAP_IRQ(handler_irq, 9) TRAP_IRQ(handler_irq, 10) -tl0_irq11: TRAP_IRQ(handler_irq, 11) TRAP_IRQ(handler_irq, 12) -tl0_irq13: TRAP_IRQ(handler_irq, 13) +tl0_irq5: TRAP_IRQ(handler_irq, 5) +tl0_irq6: BTRAP(0x46) BTRAP(0x47) BTRAP(0x48) BTRAP(0x49) +tl0_irq10: BTRAP(0x4a) BTRAP(0x4b) BTRAP(0x4c) BTRAP(0x4d) #ifndef CONFIG_SMP -tl0_irq14: TRAP_IRQ(handler_irq, 14) +tl0_irq14: TRAP_IRQ(timer_irq, 14) #else tl0_irq14: TICK_SMP_IRQ #endif diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c index 001e8518331..a9b765271b8 100644 --- a/arch/sparc64/kernel/unaligned.c +++ b/arch/sparc64/kernel/unaligned.c @@ -20,6 +20,7 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/bitops.h> +#include <linux/kallsyms.h> #include <asm/fpumacro.h> /* #define DEBUG_MNA */ @@ -279,12 +280,22 @@ static void kernel_mna_trap_fault(void) asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn) { + static unsigned long count, last_time; enum direction dir = decode_direction(insn); int size = decode_access_size(insn); current_thread_info()->kern_una_regs = regs; current_thread_info()->kern_una_insn = insn; + if (jiffies - last_time > 5 * HZ) + count = 0; + if (count < 5) { + last_time = jiffies; + count++; + printk("Kernel unaligned access at TPC[%lx] ", regs->tpc); + print_symbol("%s\n", regs->tpc); + } + if (!ok_for_kernel(insn) || dir == both) { printk("Unsupported unaligned load/store trap for kernel " "at <%016lx>.\n", regs->tpc); diff --git a/arch/sparc64/lib/atomic.S b/arch/sparc64/lib/atomic.S index faf87c31598..9633750167d 100644 --- a/arch/sparc64/lib/atomic.S +++ b/arch/sparc64/lib/atomic.S @@ -4,7 +4,6 @@ * Copyright (C) 1999 David S. Miller (davem@redhat.com) */ -#include <linux/config.h> #include <asm/asi.h> .text diff --git a/arch/sparc64/lib/bitops.S b/arch/sparc64/lib/bitops.S index 31afbfe6c1e..892431a8213 100644 --- a/arch/sparc64/lib/bitops.S +++ b/arch/sparc64/lib/bitops.S @@ -4,7 +4,6 @@ * Copyright (C) 2000 David S. Miller (davem@redhat.com) */ -#include <linux/config.h> #include <asm/asi.h> .text diff --git a/arch/sparc64/lib/mcount.S b/arch/sparc64/lib/mcount.S index 2ef2e268bdc..9e4534b485c 100644 --- a/arch/sparc64/lib/mcount.S +++ b/arch/sparc64/lib/mcount.S @@ -5,7 +5,6 @@ * This can also be tweaked for kernel stack overflow detection. */ -#include <linux/config.h> #include <linux/linkage.h> #include <asm/ptrace.h> diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index 6e002aacb96..1605967cce9 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -31,6 +31,40 @@ #include <asm/kdebug.h> #include <asm/mmu_context.h> +#ifdef CONFIG_KPROBES +ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain); + +/* Hook to register for page fault notifications */ +int register_page_fault_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(¬ify_page_fault_chain, nb); +} + +int unregister_page_fault_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(¬ify_page_fault_chain, nb); +} + +static inline int notify_page_fault(enum die_val val, const char *str, + struct pt_regs *regs, long err, int trap, int sig) +{ + struct die_args args = { + .regs = regs, + .str = str, + .err = err, + .trapnr = trap, + .signr = sig + }; + return atomic_notifier_call_chain(¬ify_page_fault_chain, val, &args); +} +#else +static inline int notify_page_fault(enum die_val val, const char *str, + struct pt_regs *regs, long err, int trap, int sig) +{ + return NOTIFY_DONE; +} +#endif + /* * To debug kernel to catch accesses to certain virtual/physical addresses. * Mode = 0 selects physical watchpoints, mode = 1 selects virtual watchpoints. @@ -263,7 +297,7 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) fault_code = get_thread_fault_code(); - if (notify_die(DIE_PAGE_FAULT, "page_fault", regs, + if (notify_page_fault(DIE_PAGE_FAULT, "page_fault", regs, fault_code, 0, SIGSEGV) == NOTIFY_STOP) return; diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c index fbbbebbad8a..53b9b1f528e 100644 --- a/arch/sparc64/mm/hugetlbpage.c +++ b/arch/sparc64/mm/hugetlbpage.c @@ -4,7 +4,6 @@ * Copyright (C) 2002, 2003, 2006 David S. Miller (davem@davemloft.net) */ -#include <linux/config.h> #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 1539a8362b6..dcba4e6ab57 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -5,7 +5,6 @@ * Copyright (C) 1997-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -18,6 +17,7 @@ #include <linux/initrd.h> #include <linux/swap.h> #include <linux/pagemap.h> +#include <linux/poison.h> #include <linux/fs.h> #include <linux/seq_file.h> #include <linux/kprobes.h> @@ -42,6 +42,7 @@ #include <asm/sections.h> #include <asm/tsb.h> #include <asm/hypervisor.h> +#include <asm/prom.h> extern void device_scan(void); @@ -101,8 +102,6 @@ static void __init read_obp_memory(const char *property, prom_halt(); } - *num_ents = ents; - /* Sanitize what we got from the firmware, by page aligning * everything. */ @@ -124,6 +123,25 @@ static void __init read_obp_memory(const char *property, regs[i].phys_addr = base; regs[i].reg_size = size; } + + for (i = 0; i < ents; i++) { + if (regs[i].reg_size == 0UL) { + int j; + + for (j = i; j < ents - 1; j++) { + regs[j].phys_addr = + regs[j+1].phys_addr; + regs[j].reg_size = + regs[j+1].reg_size; + } + + ents--; + i--; + } + } + + *num_ents = ents; + sort(regs, ents, sizeof(struct linux_prom64_registers), cmp_p64, NULL); } @@ -1339,6 +1357,8 @@ void __init paging_init(void) kernel_physical_mapping_init(); + prom_build_devicetree(); + { unsigned long zones_size[MAX_NR_ZONES]; unsigned long zholes_size[MAX_NR_ZONES]; @@ -1376,7 +1396,7 @@ static void __init taint_real_pages(void) while (old_start < old_end) { int n; - for (n = 0; pavail_rescan_ents; n++) { + for (n = 0; n < pavail_rescan_ents; n++) { unsigned long new_start, new_end; new_start = pavail_rescan[n].phys_addr; @@ -1398,6 +1418,32 @@ static void __init taint_real_pages(void) } } +int __init page_in_phys_avail(unsigned long paddr) +{ + int i; + + paddr &= PAGE_MASK; + + for (i = 0; i < pavail_rescan_ents; i++) { + unsigned long start, end; + + start = pavail_rescan[i].phys_addr; + end = start + pavail_rescan[i].reg_size; + + if (paddr >= start && paddr < end) + return 1; + } + if (paddr >= kern_base && paddr < (kern_base + kern_size)) + return 1; +#ifdef CONFIG_BLK_DEV_INITRD + if (paddr >= __pa(initrd_start) && + paddr < __pa(PAGE_ALIGN(initrd_end))) + return 1; +#endif + + return 0; +} + void __init mem_init(void) { unsigned long codepages, datapages, initpages; @@ -1474,7 +1520,7 @@ void free_initmem(void) page = (addr + ((unsigned long) __va(kern_base)) - ((unsigned long) KERNBASE)); - memset((void *)addr, 0xcc, PAGE_SIZE); + memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE); p = virt_to_page(page); ClearPageReserved(p); @@ -1522,6 +1568,7 @@ pgprot_t PAGE_EXEC __read_mostly; unsigned long pg_iobits __read_mostly; unsigned long _PAGE_IE __read_mostly; +EXPORT_SYMBOL(_PAGE_IE); unsigned long _PAGE_E __read_mostly; EXPORT_SYMBOL(_PAGE_E); diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S index f8479fad404..d70b60a3bbc 100644 --- a/arch/sparc64/mm/ultra.S +++ b/arch/sparc64/mm/ultra.S @@ -4,7 +4,6 @@ * Copyright (C) 1997, 2000 David S. Miller (davem@redhat.com) */ -#include <linux/config.h> #include <asm/asi.h> #include <asm/pgtable.h> #include <asm/page.h> diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c index 577bde8b664..0b4213720d4 100644 --- a/arch/sparc64/prom/misc.c +++ b/arch/sparc64/prom/misc.c @@ -6,7 +6,6 @@ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> diff --git a/arch/sparc64/solaris/fs.c b/arch/sparc64/solaris/fs.c index 4885ca6cbc7..0f0eb6aa1c4 100644 --- a/arch/sparc64/solaris/fs.c +++ b/arch/sparc64/solaris/fs.c @@ -356,7 +356,7 @@ static int report_statvfs(struct vfsmount *mnt, struct inode *inode, u32 buf) int error; struct sol_statvfs __user *ss = A(buf); - error = vfs_statfs(mnt->mnt_sb, &s); + error = vfs_statfs(mnt->mnt_root, &s); if (!error) { const char *p = mnt->mnt_sb->s_type->name; int i = 0; @@ -392,7 +392,7 @@ static int report_statvfs64(struct vfsmount *mnt, struct inode *inode, u32 buf) int error; struct sol_statvfs64 __user *ss = A(buf); - error = vfs_statfs(mnt->mnt_sb, &s); + error = vfs_statfs(mnt->mnt_root, &s); if (!error) { const char *p = mnt->mnt_sb->s_type->name; int i = 0; diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index 5284996780a..8135ec322c9 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -4,7 +4,6 @@ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include <linux/config.h> #include <linux/module.h> #include <linux/types.h> #include <linux/smp_lock.h> @@ -23,6 +22,7 @@ #include <asm/oplib.h> #include <asm/idprom.h> #include <asm/smp.h> +#include <asm/prom.h> #include "conv.h" @@ -194,14 +194,17 @@ static char *machine(void) } } -static char *platform(char *buffer) +static char *platform(char *buffer, int sz) { + struct device_node *dp = of_find_node_by_path("/"); int len; *buffer = 0; - len = prom_getproperty(prom_root_node, "name", buffer, 256); - if(len > 0) - buffer[len] = 0; + len = strlen(dp->name); + if (len > sz) + len = sz; + memcpy(buffer, dp->name, len); + buffer[len] = 0; if (*buffer) { char *p; @@ -213,16 +216,22 @@ static char *platform(char *buffer) return "sun4u"; } -static char *serial(char *buffer) +static char *serial(char *buffer, int sz) { - int node = prom_getchild(prom_root_node); + struct device_node *dp = of_find_node_by_path("/options"); int len; - node = prom_searchsiblings(node, "options"); *buffer = 0; - len = prom_getproperty(node, "system-board-serial#", buffer, 256); - if(len > 0) - buffer[len] = 0; + if (dp) { + char *val = of_get_property(dp, "system-board-serial#", &len); + + if (val && len > 0) { + if (len > sz) + len = sz; + memcpy(buffer, val, len); + buffer[len] = 0; + } + } if (!*buffer) return "4512348717234"; else @@ -305,8 +314,8 @@ asmlinkage int solaris_sysinfo(int cmd, u32 buf, s32 count) case SI_MACHINE: r = machine(); break; case SI_ARCHITECTURE: r = "sparc"; break; case SI_HW_PROVIDER: r = "Sun_Microsystems"; break; - case SI_HW_SERIAL: r = serial(buffer); break; - case SI_PLATFORM: r = platform(buffer); break; + case SI_HW_SERIAL: r = serial(buffer, sizeof(buffer)); break; + case SI_PLATFORM: r = platform(buffer, sizeof(buffer)); break; case SI_SRPC_DOMAIN: r = ""; break; case SI_VERSION: r = "Generic"; break; default: return -EINVAL; diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c index fc6669e8dde..bc3df95bc05 100644 --- a/arch/sparc64/solaris/socksys.c +++ b/arch/sparc64/solaris/socksys.c @@ -26,7 +26,6 @@ #include <linux/slab.h> #include <linux/syscalls.h> #include <linux/in.h> -#include <linux/devfs_fs_kernel.h> #include <net/sock.h> @@ -190,8 +189,6 @@ init_socksys(void) return ret; } - devfs_mk_cdev(MKDEV(30, 0), S_IFCHR|S_IRUSR|S_IWUSR, "socksys"); - file = fcheck(ret); /* N.B. Is this valid? Suppose the f_ops are in a module ... */ socksys_file_ops = *file->f_op; @@ -207,5 +204,4 @@ cleanup_socksys(void) { if (unregister_chrdev(30, "socksys")) printk ("Couldn't unregister socksys character device\n"); - devfs_remove ("socksys"); } |