diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-01-31 13:37:27 +1100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-01-31 13:37:27 +1100 |
commit | 8af03e782cae1e0a0f530ddd22301cdd12cf9dc0 (patch) | |
tree | c4af13a38bd3cc1a811a37f2358491f171052070 /arch/powerpc/sysdev | |
parent | 6232665040f9a23fafd9d94d4ae8d5a2dc850f65 (diff) | |
parent | 99e139126ab2e84be67969650f92eb37c12ab5cd (diff) |
Merge branch 'for-2.6.25' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
* 'for-2.6.25' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc: (454 commits)
[POWERPC] Cell IOMMU fixed mapping support
[POWERPC] Split out the ioid fetching/checking logic
[POWERPC] Add support to cell_iommu_setup_page_tables() for multiple windows
[POWERPC] Split out the IOMMU logic from cell_dma_dev_setup()
[POWERPC] Split cell_iommu_setup_hardware() into two parts
[POWERPC] Split out the logic that allocates struct iommus
[POWERPC] Allocate the hash table under 1G on cell
[POWERPC] Add set_dma_ops() to match get_dma_ops()
[POWERPC] 83xx: Clean up / convert mpc83xx board DTS files to v1 format.
[POWERPC] 85xx: Only invalidate TLB0 and TLB1
[POWERPC] 83xx: Fix typo in mpc837x compatible entries
[POWERPC] 85xx: convert sbc85* boards to use machine_device_initcall
[POWERPC] 83xx: rework platform Kconfig
[POWERPC] 85xx: rework platform Kconfig
[POWERPC] 86xx: Remove unused IRQ defines
[POWERPC] QE: Explicitly set address-cells and size cells for muram
[POWERPC] Convert StorCenter DTS file to /dts-v1/ format.
[POWERPC] 86xx: Convert all 86xx DTS files to /dts-v1/ format.
[PPC] Remove 85xx from arch/ppc
[PPC] Remove 83xx from arch/ppc
...
Diffstat (limited to 'arch/powerpc/sysdev')
34 files changed, 4114 insertions, 447 deletions
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig new file mode 100644 index 00000000000..72fb35b9ebc --- /dev/null +++ b/arch/powerpc/sysdev/Kconfig @@ -0,0 +1,8 @@ +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.txt. +# + +config PPC4xx_PCI_EXPRESS + bool + depends on PCI && 4xx + default n diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 99a77d743d4..15f3e8527d7 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -2,7 +2,7 @@ ifeq ($(CONFIG_PPC64),y) EXTRA_CFLAGS += -mno-minimal-toc endif -mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o +mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) obj-$(CONFIG_PPC_MPC106) += grackle.o @@ -12,6 +12,7 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o obj-$(CONFIG_FSL_SOC) += fsl_soc.o obj-$(CONFIG_FSL_PCI) += fsl_pci.o +obj-$(CONFIG_RAPIDIO) += fsl_rio.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ @@ -24,16 +25,20 @@ obj-$(CONFIG_AXON_RAM) += axonram.o ifeq ($(CONFIG_PPC_MERGE),y) obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPC_I8259) += i8259.o -obj-$(CONFIG_PPC_83xx) += ipic.o +obj-$(CONFIG_IPIC) += ipic.o obj-$(CONFIG_4xx) += uic.o obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o +obj-$(CONFIG_OF_RTC) += of_rtc.o +ifeq ($(CONFIG_PCI),y) +obj-$(CONFIG_4xx) += ppc4xx_pci.o +endif endif # Temporary hack until we have migrated to asm-powerpc ifeq ($(ARCH),powerpc) obj-$(CONFIG_CPM) += cpm_common.o -obj-$(CONFIG_CPM2) += cpm2_common.o cpm2_pic.o +obj-$(CONFIG_CPM2) += cpm2.o cpm2_pic.o obj-$(CONFIG_PPC_DCR) += dcr.o -obj-$(CONFIG_8xx) += mpc8xx_pic.o commproc.o +obj-$(CONFIG_8xx) += mpc8xx_pic.o cpm1.o obj-$(CONFIG_UCODE_PATCH) += micropatch.o endif diff --git a/arch/powerpc/sysdev/axonram.c b/arch/powerpc/sysdev/axonram.c index 5eaf3e3f4b8..d359d6e9297 100644 --- a/arch/powerpc/sysdev/axonram.c +++ b/arch/powerpc/sysdev/axonram.c @@ -42,8 +42,9 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/types.h> -#include <asm/of_device.h> -#include <asm/of_platform.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> + #include <asm/page.h> #include <asm/prom.h> diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.c b/arch/powerpc/sysdev/bestcomm/bestcomm.c index 740ad73ce5c..f589999361e 100644 --- a/arch/powerpc/sysdev/bestcomm/bestcomm.c +++ b/arch/powerpc/sysdev/bestcomm/bestcomm.c @@ -29,11 +29,17 @@ #define DRIVER_NAME "bestcomm-core" +/* MPC5200 device tree match tables */ +static struct of_device_id mpc52xx_sram_ids[] __devinitdata = { + { .compatible = "fsl,mpc5200-sram", }, + { .compatible = "mpc5200-sram", }, + {} +}; + struct bcom_engine *bcom_eng = NULL; EXPORT_SYMBOL_GPL(bcom_eng); /* needed for inline functions */ - /* ======================================================================== */ /* Public and private API */ /* ======================================================================== */ @@ -373,7 +379,7 @@ mpc52xx_bcom_probe(struct of_device *op, const struct of_device_id *match) of_node_get(op->node); /* Prepare SRAM */ - ofn_sram = of_find_compatible_node(NULL, "sram", "mpc5200-sram"); + ofn_sram = of_find_matching_node(NULL, mpc52xx_sram_ids); if (!ofn_sram) { printk(KERN_ERR DRIVER_NAME ": " "No SRAM found in device tree\n"); @@ -478,10 +484,8 @@ mpc52xx_bcom_remove(struct of_device *op) } static struct of_device_id mpc52xx_bcom_of_match[] = { - { - .type = "dma-controller", - .compatible = "mpc5200-bestcomm", - }, + { .type = "dma-controller", .compatible = "fsl,mpc5200-bestcomm", }, + { .type = "dma-controller", .compatible = "mpc5200-bestcomm", }, {}, }; diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.h b/arch/powerpc/sysdev/bestcomm/bestcomm.h index e802cb4eb69..c960a8b4965 100644 --- a/arch/powerpc/sysdev/bestcomm/bestcomm.h +++ b/arch/powerpc/sysdev/bestcomm/bestcomm.h @@ -20,7 +20,7 @@ struct bcom_bd; /* defined later on ... */ /* ======================================================================== */ -/* Generic task managment */ +/* Generic task management */ /* ======================================================================== */ /** diff --git a/arch/powerpc/sysdev/commproc.h b/arch/powerpc/sysdev/commproc.h deleted file mode 100644 index 9155ba46727..00000000000 --- a/arch/powerpc/sysdev/commproc.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _POWERPC_SYSDEV_COMMPROC_H -#define _POWERPC_SYSDEV_COMMPROC_H - -extern void cpm_reset(void); -extern void mpc8xx_restart(char *cmd); -extern void mpc8xx_calibrate_decr(void); -extern int mpc8xx_set_rtc_time(struct rtc_time *tm); -extern void mpc8xx_get_rtc_time(struct rtc_time *tm); -extern void m8xx_pic_init(void); -extern unsigned int mpc8xx_get_irq(void); - -#endif diff --git a/arch/powerpc/sysdev/commproc.c b/arch/powerpc/sysdev/cpm1.c index f6a63780bbd..df8bd2b6479 100644 --- a/arch/powerpc/sysdev/commproc.c +++ b/arch/powerpc/sysdev/cpm1.c @@ -30,11 +30,10 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/module.h> -#include <asm/mpc8xx.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/8xx_immap.h> -#include <asm/commproc.h> +#include <asm/cpm1.h> #include <asm/io.h> #include <asm/tlbflush.h> #include <asm/rheap.h> @@ -48,8 +47,6 @@ #ifndef CONFIG_PPC_CPM_NEW_BINDING static void m8xx_cpm_dpinit(void); #endif -static uint host_buffer; /* One page of host buffer */ -static uint host_end; /* end + 1 */ cpm8xx_t __iomem *cpmp; /* Pointer to comm processor space */ immap_t __iomem *mpc8xx_immr; static cpic8xx_t __iomem *cpic_reg; @@ -240,40 +237,33 @@ void __init cpm_reset(void) #endif } -/* We used to do this earlier, but have to postpone as long as possible - * to ensure the kernel VM is now running. - */ -static void -alloc_host_memory(void) -{ - dma_addr_t physaddr; +static DEFINE_SPINLOCK(cmd_lock); - /* Set the host page for allocation. - */ - host_buffer = (uint)dma_alloc_coherent(NULL, PAGE_SIZE, &physaddr, - GFP_KERNEL); - host_end = host_buffer + PAGE_SIZE; -} +#define MAX_CR_CMD_LOOPS 10000 -/* We also own one page of host buffer space for the allocation of - * UART "fifos" and the like. - */ -uint -m8xx_cpm_hostalloc(uint size) +int cpm_command(u32 command, u8 opcode) { - uint retloc; + int i, ret; + unsigned long flags; - if (host_buffer == 0) - alloc_host_memory(); + if (command & 0xffffff0f) + return -EINVAL; - if ((host_buffer + size) >= host_end) - return(0); + spin_lock_irqsave(&cmd_lock, flags); - retloc = host_buffer; - host_buffer += size; + ret = 0; + out_be16(&cpmp->cp_cpcr, command | CPM_CR_FLG | (opcode << 8)); + for (i = 0; i < MAX_CR_CMD_LOOPS; i++) + if ((in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG) == 0) + goto out; - return(retloc); + printk(KERN_ERR "%s(): Not able to issue CPM command\n", __FUNCTION__); + ret = -EIO; +out: + spin_unlock_irqrestore(&cmd_lock, flags); + return ret; } +EXPORT_SYMBOL(cpm_command); /* Set a baud rate generator. This needs lots of work. There are * four BRGs, any of which can be wired to any channel. @@ -300,7 +290,7 @@ cpm_setbrg(uint brg, uint rate) out_be32(bp, (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN); else out_be32(bp, (((BRG_UART_CLK_DIV16 / rate) - 1) << 1) | - CPM_BRG_EN | CPM_BRG_DIV16); + CPM_BRG_EN | CPM_BRG_DIV16); } #ifndef CONFIG_PPC_CPM_NEW_BINDING @@ -408,7 +398,7 @@ EXPORT_SYMBOL(cpm_dpram_phys); #endif /* !CONFIG_PPC_CPM_NEW_BINDING */ struct cpm_ioport16 { - __be16 dir, par, sor, dat, intr; + __be16 dir, par, odr_sor, dat, intr; __be16 res[3]; }; @@ -438,6 +428,13 @@ static void cpm1_set_pin32(int port, int pin, int flags) else clrbits32(&iop->par, pin); + if (port == CPM_PORTB) { + if (flags & CPM_PIN_OPENDRAIN) + setbits16(&mpc8xx_immr->im_cpm.cp_pbodr, pin); + else + clrbits16(&mpc8xx_immr->im_cpm.cp_pbodr, pin); + } + if (port == CPM_PORTE) { if (flags & CPM_PIN_SECONDARY) setbits32(&iop->sor, pin); @@ -471,11 +468,17 @@ static void cpm1_set_pin16(int port, int pin, int flags) else clrbits16(&iop->par, pin); + if (port == CPM_PORTA) { + if (flags & CPM_PIN_OPENDRAIN) + setbits16(&iop->odr_sor, pin); + else + clrbits16(&iop->odr_sor, pin); + } if (port == CPM_PORTC) { if (flags & CPM_PIN_SECONDARY) - setbits16(&iop->sor, pin); + setbits16(&iop->odr_sor, pin); else - clrbits16(&iop->sor, pin); + clrbits16(&iop->odr_sor, pin); } } diff --git a/arch/powerpc/sysdev/cpm2_common.c b/arch/powerpc/sysdev/cpm2.c index c1d82403202..7be71123212 100644 --- a/arch/powerpc/sysdev/cpm2_common.c +++ b/arch/powerpc/sysdev/cpm2.c @@ -82,6 +82,31 @@ void __init cpm2_reset(void) cpmp = &cpm2_immr->im_cpm; } +static DEFINE_SPINLOCK(cmd_lock); + +#define MAX_CR_CMD_LOOPS 10000 + +int cpm_command(u32 command, u8 opcode) +{ + int i, ret; + unsigned long flags; + + spin_lock_irqsave(&cmd_lock, flags); + + ret = 0; + out_be32(&cpmp->cp_cpcr, command | opcode | CPM_CR_FLG); + for (i = 0; i < MAX_CR_CMD_LOOPS; i++) + if ((in_be32(&cpmp->cp_cpcr) & CPM_CR_FLG) == 0) + goto out; + + printk(KERN_ERR "%s(): Not able to issue CPM command\n", __FUNCTION__); + ret = -EIO; +out: + spin_unlock_irqrestore(&cmd_lock, flags); + return ret; +} +EXPORT_SYMBOL(cpm_command); + /* Set a baud rate generator. This needs lots of work. There are * eight BRGs, which can be connected to the CPM channels or output * as clocks. The BRGs are in two different block of internal @@ -128,8 +153,7 @@ cpm2_fastbrg(uint brg, uint rate, int div16) if (brg < 4) { bp = cpm2_map_size(im_brgc1, 16); - } - else { + } else { bp = cpm2_map_size(im_brgc5, 16); brg -= 4; } diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index 33df4c347ca..bf13c2174a4 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -33,8 +33,8 @@ void __init setup_pci_atmu(struct pci_controller *hose, struct resource *rsrc) struct ccsr_pci __iomem *pci; int i; - pr_debug("PCI memory map start 0x%x, size 0x%x\n", rsrc->start, - rsrc->end - rsrc->start + 1); + pr_debug("PCI memory map start 0x%016llx, size 0x%016llx\n", + (u64)rsrc->start, (u64)rsrc->end - (u64)rsrc->start + 1); pci = ioremap(rsrc->start, rsrc->end - rsrc->start + 1); /* Disable all windows (except powar0 since its ignored) */ @@ -46,17 +46,17 @@ void __init setup_pci_atmu(struct pci_controller *hose, struct resource *rsrc) /* Setup outbound MEM window */ for(i = 0; i < 3; i++) if (hose->mem_resources[i].flags & IORESOURCE_MEM){ - pr_debug("PCI MEM resource start 0x%08x, size 0x%08x.\n", - hose->mem_resources[i].start, - hose->mem_resources[i].end - - hose->mem_resources[i].start + 1); - out_be32(&pci->pow[i+1].potar, - (hose->mem_resources[i].start >> 12) - & 0x000fffff); + resource_size_t pci_addr_start = + hose->mem_resources[i].start - + hose->pci_mem_offset; + pr_debug("PCI MEM resource start 0x%016llx, size 0x%016llx.\n", + (u64)hose->mem_resources[i].start, + (u64)hose->mem_resources[i].end + - (u64)hose->mem_resources[i].start + 1); + out_be32(&pci->pow[i+1].potar, (pci_addr_start >> 12)); out_be32(&pci->pow[i+1].potear, 0); out_be32(&pci->pow[i+1].powbar, - (hose->mem_resources[i].start >> 12) - & 0x000fffff); + (hose->mem_resources[i].start >> 12)); /* Enable, Mem R/W */ out_be32(&pci->pow[i+1].powar, 0x80044000 | (__ilog2(hose->mem_resources[i].end @@ -65,15 +65,14 @@ void __init setup_pci_atmu(struct pci_controller *hose, struct resource *rsrc) /* Setup outbound IO window */ if (hose->io_resource.flags & IORESOURCE_IO){ - pr_debug("PCI IO resource start 0x%08x, size 0x%08x, phy base 0x%08x.\n", - hose->io_resource.start, - hose->io_resource.end - hose->io_resource.start + 1, - hose->io_base_phys); - out_be32(&pci->pow[i+1].potar, (hose->io_resource.start >> 12) - & 0x000fffff); + pr_debug("PCI IO resource start 0x%016llx, size 0x%016llx, " + "phy base 0x%016llx.\n", + (u64)hose->io_resource.start, + (u64)hose->io_resource.end - (u64)hose->io_resource.start + 1, + (u64)hose->io_base_phys); + out_be32(&pci->pow[i+1].potar, (hose->io_resource.start >> 12)); out_be32(&pci->pow[i+1].potear, 0); - out_be32(&pci->pow[i+1].powbar, (hose->io_base_phys >> 12) - & 0x000fffff); + out_be32(&pci->pow[i+1].powbar, (hose->io_base_phys >> 12)); /* Enable, IO R/W */ out_be32(&pci->pow[i+1].powar, 0x80088000 | (__ilog2(hose->io_resource.end @@ -107,55 +106,17 @@ void __init setup_pci_cmd(struct pci_controller *hose) } } -static void __init quirk_fsl_pcie_transparent(struct pci_dev *dev) -{ - struct resource *res; - int i, res_idx = PCI_BRIDGE_RESOURCES; - struct pci_controller *hose; +static int fsl_pcie_bus_fixup; +static void __init quirk_fsl_pcie_header(struct pci_dev *dev) +{ /* if we aren't a PCIe don't bother */ if (!pci_find_capability(dev, PCI_CAP_ID_EXP)) return ; - /* - * Make the bridge be transparent. - */ - dev->transparent = 1; - - hose = pci_bus_to_host(dev->bus); - if (!hose) { - printk(KERN_ERR "Can't find hose for bus %d\n", - dev->bus->number); - return; - } - - /* Clear out any of the virtual P2P bridge registers */ - pci_write_config_word(dev, PCI_IO_BASE_UPPER16, 0); - pci_write_config_word(dev, PCI_IO_LIMIT_UPPER16, 0); - pci_write_config_byte(dev, PCI_IO_BASE, 0x10); - pci_write_config_byte(dev, PCI_IO_LIMIT, 0); - pci_write_config_word(dev, PCI_MEMORY_BASE, 0x10); - pci_write_config_word(dev, PCI_MEMORY_LIMIT, 0); - pci_write_config_word(dev, PCI_PREF_BASE_UPPER32, 0x0); - pci_write_config_word(dev, PCI_PREF_LIMIT_UPPER32, 0x0); - pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, 0x10); - pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, 0); - - if (hose->io_resource.flags) { - res = &dev->resource[res_idx++]; - res->start = hose->io_resource.start; - res->end = hose->io_resource.end; - res->flags = hose->io_resource.flags; - update_bridge_resource(dev, res); - } - - for (i = 0; i < 3; i++) { - res = &dev->resource[res_idx + i]; - res->start = hose->mem_resources[i].start; - res->end = hose->mem_resources[i].end; - res->flags = hose->mem_resources[i].flags; - update_bridge_resource(dev, res); - } + dev->class = PCI_CLASS_BRIDGE_PCI << 8; + fsl_pcie_bus_fixup = 1; + return ; } int __init fsl_pcie_check_link(struct pci_controller *hose) @@ -172,11 +133,24 @@ void fsl_pcibios_fixup_bus(struct pci_bus *bus) struct pci_controller *hose = (struct pci_controller *) bus->sysdata; int i; - /* deal with bogus pci_bus when we don't have anything connected on PCIe */ - if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK) { - if (bus->parent) { - for (i = 0; i < 4; ++i) - bus->resource[i] = bus->parent->resource[i]; + if ((bus->parent == hose->bus) && + ((fsl_pcie_bus_fixup && + early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) || + (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK))) + { + for (i = 0; i < 4; ++i) { + struct resource *res = bus->resource[i]; + struct resource *par = bus->parent->resource[i]; + if (res) { + res->start = 0; + res->end = 0; + res->flags = 0; + } + if (res && par) { + res->start = par->start; + res->end = par->end; + res->flags = par->flags; + } } } } @@ -202,7 +176,7 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary) printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); - pci_assign_all_buses = 1; + ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS; hose = pcibios_alloc_controller(dev); if (!hose) return -ENOMEM; @@ -222,7 +196,7 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary) hose->indirect_type |= PPC_INDIRECT_TYPE_NO_PCIE_LINK; } - printk(KERN_INFO "Found FSL PCI host bridge at 0x%016llx." + printk(KERN_INFO "Found FSL PCI host bridge at 0x%016llx. " "Firmware bus number: %d->%d\n", (unsigned long long)rsrc.start, hose->first_busno, hose->last_busno); @@ -240,23 +214,23 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary) return 0; } -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8548E, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8548, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8543E, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8543, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8547E, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8545E, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8545, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8568E, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8568, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8567E, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8567, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8533E, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8533, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8544E, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8544, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8572E, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8572, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8641, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8641D, quirk_fsl_pcie_transparent); -DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8610, quirk_fsl_pcie_transparent); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8548E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8548, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8543E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8543, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8547E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8545E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8545, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8568E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8568, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8567E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8567, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8533E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8533, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8544E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8544, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8572E, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8572, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8641, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8641D, quirk_fsl_pcie_header); +DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8610, quirk_fsl_pcie_header); diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c new file mode 100644 index 00000000000..af2425e4655 --- /dev/null +++ b/arch/powerpc/sysdev/fsl_rio.c @@ -0,0 +1,932 @@ +/* + * MPC85xx RapidIO support + * + * Copyright 2005 MontaVista Software, Inc. + * Matt Porter <mporter@kernel.crashing.org> + * + * 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/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/rio.h> +#include <linux/rio_drv.h> + +#include <asm/io.h> + +#define RIO_REGS_BASE (CCSRBAR + 0xc0000) +#define RIO_ATMU_REGS_OFFSET 0x10c00 +#define RIO_MSG_REGS_OFFSET 0x11000 +#define RIO_MAINT_WIN_SIZE 0x400000 +#define RIO_DBELL_WIN_SIZE 0x1000 + +#define RIO_MSG_OMR_MUI 0x00000002 +#define RIO_MSG_OSR_TE 0x00000080 +#define RIO_MSG_OSR_QOI 0x00000020 +#define RIO_MSG_OSR_QFI 0x00000010 +#define RIO_MSG_OSR_MUB 0x00000004 +#define RIO_MSG_OSR_EOMI 0x00000002 +#define RIO_MSG_OSR_QEI 0x00000001 + +#define RIO_MSG_IMR_MI 0x00000002 +#define RIO_MSG_ISR_TE 0x00000080 +#define RIO_MSG_ISR_QFI 0x00000010 +#define RIO_MSG_ISR_DIQI 0x00000001 + +#define RIO_MSG_DESC_SIZE 32 +#define RIO_MSG_BUFFER_SIZE 4096 +#define RIO_MIN_TX_RING_SIZE 2 +#define RIO_MAX_TX_RING_SIZE 2048 +#define RIO_MIN_RX_RING_SIZE 2 +#define RIO_MAX_RX_RING_SIZE 2048 + +#define DOORBELL_DMR_DI 0x00000002 +#define DOORBELL_DSR_TE 0x00000080 +#define DOORBELL_DSR_QFI 0x00000010 +#define DOORBELL_DSR_DIQI 0x00000001 +#define DOORBELL_TID_OFFSET 0x03 +#define DOORBELL_SID_OFFSET 0x05 +#define DOORBELL_INFO_OFFSET 0x06 + +#define DOORBELL_MESSAGE_SIZE 0x08 +#define DBELL_SID(x) (*(u8 *)(x + DOORBELL_SID_OFFSET)) +#define DBELL_TID(x) (*(u8 *)(x + DOORBELL_TID_OFFSET)) +#define DBELL_INF(x) (*(u16 *)(x + DOORBELL_INFO_OFFSET)) + +struct rio_atmu_regs { + u32 rowtar; + u32 pad1; + u32 rowbar; + u32 pad2; + u32 rowar; + u32 pad3[3]; +}; + +struct rio_msg_regs { + u32 omr; + u32 osr; + u32 pad1; + u32 odqdpar; + u32 pad2; + u32 osar; + u32 odpr; + u32 odatr; + u32 odcr; + u32 pad3; + u32 odqepar; + u32 pad4[13]; + u32 imr; + u32 isr; + u32 pad5; + u32 ifqdpar; + u32 pad6; + u32 ifqepar; + u32 pad7[250]; + u32 dmr; + u32 dsr; + u32 pad8; + u32 dqdpar; + u32 pad9; + u32 dqepar; + u32 pad10[26]; + u32 pwmr; + u32 pwsr; + u32 pad11; + u32 pwqbar; +}; + +struct rio_tx_desc { + u32 res1; + u32 saddr; + u32 dport; + u32 dattr; + u32 res2; + u32 res3; + u32 dwcnt; + u32 res4; +}; + +static u32 regs_win; +static struct rio_atmu_regs *atmu_regs; +static struct rio_atmu_regs *maint_atmu_regs; +static struct rio_atmu_regs *dbell_atmu_regs; +static u32 dbell_win; +static u32 maint_win; +static struct rio_msg_regs *msg_regs; + +static struct rio_dbell_ring { + void *virt; + dma_addr_t phys; +} dbell_ring; + +static struct rio_msg_tx_ring { + void *virt; + dma_addr_t phys; + void *virt_buffer[RIO_MAX_TX_RING_SIZE]; + dma_addr_t phys_buffer[RIO_MAX_TX_RING_SIZE]; + int tx_slot; + int size; + void *dev_id; +} msg_tx_ring; + +static struct rio_msg_rx_ring { + void *virt; + dma_addr_t phys; + void *virt_buffer[RIO_MAX_RX_RING_SIZE]; + int rx_slot; + int size; + void *dev_id; +} msg_rx_ring; + +/** + * mpc85xx_rio_doorbell_send - Send a MPC85xx doorbell message + * @index: ID of RapidIO interface + * @destid: Destination ID of target device + * @data: 16-bit info field of RapidIO doorbell message + * + * Sends a MPC85xx doorbell message. Returns %0 on success or + * %-EINVAL on failure. + */ +static int mpc85xx_rio_doorbell_send(int index, u16 destid, u16 data) +{ + pr_debug("mpc85xx_doorbell_send: index %d destid %4.4x data %4.4x\n", + index, destid, data); + out_be32((void *)&dbell_atmu_regs->rowtar, destid << 22); + out_be16((void *)(dbell_win), data); + + return 0; +} + +/** + * mpc85xx_local_config_read - Generate a MPC85xx local config space read + * @index: ID of RapdiIO interface + * @offset: Offset into configuration space + * @len: Length (in bytes) of the maintenance transaction + * @data: Value to be read into + * + * Generates a MPC85xx local configuration space read. Returns %0 on + * success or %-EINVAL on failure. + */ +static int mpc85xx_local_config_read(int index, u32 offset, int len, u32 * data) +{ + pr_debug("mpc85xx_local_config_read: index %d offset %8.8x\n", index, + offset); + *data = in_be32((void *)(regs_win + offset)); + + return 0; +} + +/** + * mpc85xx_local_config_write - Generate a MPC85xx local config space write + * @index: ID of RapdiIO interface + * @offset: Offset into configuration space + * @len: Length (in bytes) of the maintenance transaction + * @data: Value to be written + * + * Generates a MPC85xx local configuration space write. Returns %0 on + * success or %-EINVAL on failure. + */ +static int mpc85xx_local_config_write(int index, u32 offset, int len, u32 data) +{ + pr_debug + ("mpc85xx_local_config_write: index %d offset %8.8x data %8.8x\n", + index, offset, data); + out_be32((void *)(regs_win + offset), data); + + return 0; +} + +/** + * mpc85xx_rio_config_read - Generate a MPC85xx read maintenance transaction + * @index: ID of RapdiIO interface + * @destid: Destination ID of transaction + * @hopcount: Number of hops to target device + * @offset: Offset into configuration space + * @len: Length (in bytes) of the maintenance transaction + * @val: Location to be read into + * + * Generates a MPC85xx read maintenance transaction. Returns %0 on + * success or %-EINVAL on failure. + */ +static int +mpc85xx_rio_config_read(int index, u16 destid, u8 hopcount, u32 offset, int len, + u32 * val) +{ + u8 *data; + + pr_debug + ("mpc85xx_rio_config_read: index %d destid %d hopcount %d offset %8.8x len %d\n", + index, destid, hopcount, offset, len); + out_be32((void *)&maint_atmu_regs->rowtar, + (destid << 22) | (hopcount << 12) | ((offset & ~0x3) >> 9)); + + data = (u8 *) maint_win + offset; + switch (len) { + case 1: + *val = in_8((u8 *) data); + break; + case 2: + *val = in_be16((u16 *) data); + break; + default: + *val = in_be32((u32 *) data); + break; + } + + return 0; +} + +/** + * mpc85xx_rio_config_write - Generate a MPC85xx write maintenance transaction + * @index: ID of RapdiIO interface + * @destid: Destination ID of transaction + * @hopcount: Number of hops to target device + * @offset: Offset into configuration space + * @len: Length (in bytes) of the maintenance transaction + * @val: Value to be written + * + * Generates an MPC85xx write maintenance transaction. Returns %0 on + * success or %-EINVAL on failure. + */ +static int +mpc85xx_rio_config_write(int index, u16 destid, u8 hopcount, u32 offset, + int len, u32 val) +{ + u8 *data; + pr_debug + ("mpc85xx_rio_config_write: index %d destid %d hopcount %d offset %8.8x len %d val %8.8x\n", + index, destid, hopcount, offset, len, val); + out_be32((void *)&maint_atmu_regs->rowtar, + (destid << 22) | (hopcount << 12) | ((offset & ~0x3) >> 9)); + + data = (u8 *) maint_win + offset; + switch (len) { + case 1: + out_8((u8 *) data, val); + break; + case 2: + out_be16((u16 *) data, val); + break; + default: + out_be32((u32 *) data, val); + break; + } + + return 0; +} + +/** + * rio_hw_add_outb_message - Add message to the MPC85xx outbound message queue + * @mport: Master port with outbound message queue + * @rdev: Target of outbound message + * @mbox: Outbound mailbox + * @buffer: Message to add to outbound queue + * @len: Length of message + * + * Adds the @buffer message to the MPC85xx outbound message queue. Returns + * %0 on success or %-EINVAL on failure. + */ +int +rio_hw_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox, + void *buffer, size_t len) +{ + u32 omr; + struct rio_tx_desc *desc = + (struct rio_tx_desc *)msg_tx_ring.virt + msg_tx_ring.tx_slot; + int ret = 0; + + pr_debug + ("RIO: rio_hw_add_outb_message(): destid %4.4x mbox %d buffer %8.8x len %8.8x\n", + rdev->destid, mbox, (int)buffer, len); + + if ((len < 8) || (len > RIO_MAX_MSG_SIZE)) { + ret = -EINVAL; + goto out; + } + + /* Copy and clear rest of buffer */ + memcpy(msg_tx_ring.virt_buffer[msg_tx_ring.tx_slot], buffer, len); + if (len < (RIO_MAX_MSG_SIZE - 4)) + memset((void *)((u32) msg_tx_ring. + virt_buffer[msg_tx_ring.tx_slot] + len), 0, + RIO_MAX_MSG_SIZE - len); + + /* Set mbox field for message */ + desc->dport = mbox & 0x3; + + /* Enable EOMI interrupt, set priority, and set destid */ + desc->dattr = 0x28000000 | (rdev->destid << 2); + + /* Set transfer size aligned to next power of 2 (in double words) */ + desc->dwcnt = is_power_of_2(len) ? len : 1 << get_bitmask_order(len); + + /* Set snooping and source buffer address */ + desc->saddr = 0x00000004 | msg_tx_ring.phys_buffer[msg_tx_ring.tx_slot]; + + /* Increment enqueue pointer */ + omr = in_be32((void *)&msg_regs->omr); + out_be32((void *)&msg_regs->omr, omr | RIO_MSG_OMR_MUI); + + /* Go to next descriptor */ + if (++msg_tx_ring.tx_slot == msg_tx_ring.size) + msg_tx_ring.tx_slot = 0; + + out: + return ret; +} + +EXPORT_SYMBOL_GPL(rio_hw_add_outb_message); + +/** + * mpc85xx_rio_tx_handler - MPC85xx outbound message interrupt handler + * @irq: Linux interrupt number + * @dev_instance: Pointer to interrupt-specific data + * + * Handles outbound message interrupts. Executes a register outbound + * mailbox event handler and acks the interrupt occurrence. + */ +static irqreturn_t +mpc85xx_rio_tx_handler(int irq, void *dev_instance) +{ + int osr; + struct rio_mport *port = (struct rio_mport *)dev_instance; + + osr = in_be32((void *)&msg_regs->osr); + + if (osr & RIO_MSG_OSR_TE) { + pr_info("RIO: outbound message transmission error\n"); + out_be32((void *)&msg_regs->osr, RIO_MSG_OSR_TE); + goto out; + } + + if (osr & RIO_MSG_OSR_QOI) { + pr_info("RIO: outbound message queue overflow\n"); + out_be32((void *)&msg_regs->osr, RIO_MSG_OSR_QOI); + goto out; + } + + if (osr & RIO_MSG_OSR_EOMI) { + u32 dqp = in_be32((void *)&msg_regs->odqdpar); + int slot = (dqp - msg_tx_ring.phys) >> 5; + port->outb_msg[0].mcback(port, msg_tx_ring.dev_id, -1, slot); + + /* Ack the end-of-message interrupt */ + out_be32((void *)&msg_regs->osr, RIO_MSG_OSR_EOMI); + } + + out: + return IRQ_HANDLED; +} + +/** + * rio_open_outb_mbox - Initialize MPC85xx outbound mailbox + * @mport: Master port implementing the outbound message unit + * @dev_id: Device specific pointer to pass on event + * @mbox: Mailbox to open + * @entries: Number of entries in the outbound mailbox ring + * + * Initializes buffer ring, request the outbound message interrupt, + * and enables the outbound message unit. Returns %0 on success and + * %-EINVAL or %-ENOMEM on failure. + */ +int rio_open_outb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries) +{ + int i, j, rc = 0; + + if ((entries < RIO_MIN_TX_RING_SIZE) || + (entries > RIO_MAX_TX_RING_SIZE) || (!is_power_of_2(entries))) { + rc = -EINVAL; + goto out; + } + + /* Initialize shadow copy ring */ + msg_tx_ring.dev_id = dev_id; + msg_tx_ring.size = entries; + + for (i = 0; i < msg_tx_ring.size; i++) { + if (! + (msg_tx_ring.virt_buffer[i] = + dma_alloc_coherent(NULL, RIO_MSG_BUFFER_SIZE, + &msg_tx_ring.phys_buffer[i], + GFP_KERNEL))) { + rc = -ENOMEM; + for (j = 0; j < msg_tx_ring.size; j++) + if (msg_tx_ring.virt_buffer[j]) + dma_free_coherent(NULL, + RIO_MSG_BUFFER_SIZE, + msg_tx_ring. + virt_buffer[j], + msg_tx_ring. + phys_buffer[j]); + goto out; + } + } + + /* Initialize outbound message descriptor ring */ + if (!(msg_tx_ring.virt = dma_alloc_coherent(NULL, + msg_tx_ring.size * + RIO_MSG_DESC_SIZE, + &msg_tx_ring.phys, + GFP_KERNEL))) { + rc = -ENOMEM; + goto out_dma; + } + memset(msg_tx_ring.virt, 0, msg_tx_ring.size * RIO_MSG_DESC_SIZE); + msg_tx_ring.tx_slot = 0; + + /* Point dequeue/enqueue pointers at first entry in ring */ + out_be32((void *)&msg_regs->odqdpar, msg_tx_ring.phys); + out_be32((void *)&msg_regs->odqepar, msg_tx_ring.phys); + + /* Configure for snooping */ + out_be32((void *)&msg_regs->osar, 0x00000004); + + /* Clear interrupt status */ + out_be32((void *)&msg_regs->osr, 0x000000b3); + + /* Hook up outbound message handler */ + if ((rc = + request_irq(MPC85xx_IRQ_RIO_TX, mpc85xx_rio_tx_handler, 0, + "msg_tx", (void *)mport)) < 0) + goto out_irq; + + /* + * Configure outbound message unit + * Snooping + * Interrupts (all enabled, except QEIE) + * Chaining mode + * Disable + */ + out_be32((void *)&msg_regs->omr, 0x00100220); + + /* Set number of entries */ + out_be32((void *)&msg_regs->omr, + in_be32((void *)&msg_regs->omr) | + ((get_bitmask_order(entries) - 2) << 12)); + + /* Now enable the unit */ + out_be32((void *)&msg_regs->omr, in_be32((void *)&msg_regs->omr) | 0x1); + + out: + return rc; + + out_irq: + dma_free_coherent(NULL, msg_tx_ring.size * RIO_MSG_DESC_SIZE, + msg_tx_ring.virt, msg_tx_ring.phys); + + out_dma: + for (i = 0; i < msg_tx_ring.size; i++) + dma_free_coherent(NULL, RIO_MSG_BUFFER_SIZE, + msg_tx_ring.virt_buffer[i], + msg_tx_ring.phys_buffer[i]); + + return rc; +} + +/** + * rio_close_outb_mbox - Shut down MPC85xx outbound mailbox + * @mport: Master port implementing the outbound message unit + * @mbox: Mailbox to close + * + * Disables the outbound message unit, free all buffers, and + * frees the outbound message interrupt. + */ +void rio_close_outb_mbox(struct rio_mport *mport, int mbox) +{ + /* Disable inbound message unit */ + out_be32((void *)&msg_regs->omr, 0); + + /* Free ring */ + dma_free_coherent(NULL, msg_tx_ring.size * RIO_MSG_DESC_SIZE, + msg_tx_ring.virt, msg_tx_ring.phys); + + /* Free interrupt */ + free_irq(MPC85xx_IRQ_RIO_TX, (void *)mport); +} + +/** + * mpc85xx_rio_rx_handler - MPC85xx inbound message interrupt handler + * @irq: Linux interrupt number + * @dev_instance: Pointer to interrupt-specific data + * + * Handles inbound message interrupts. Executes a registered inbound + * mailbox event handler and acks the interrupt occurrence. + */ +static irqreturn_t +mpc85xx_rio_rx_handler(int irq, void *dev_instance) +{ + int isr; + struct rio_mport *port = (struct rio_mport *)dev_instance; + + isr = in_be32((void *)&msg_regs->isr); + + if (isr & RIO_MSG_ISR_TE) { + pr_info("RIO: inbound message reception error\n"); + out_be32((void *)&msg_regs->isr, RIO_MSG_ISR_TE); + goto out; + } + + /* XXX Need to check/dispatch until queue empty */ + if (isr & RIO_MSG_ISR_DIQI) { + /* + * We implement *only* mailbox 0, but can receive messages + * for any mailbox/letter to that mailbox destination. So, + * make the callback with an unknown/invalid mailbox number + * argument. + */ + port->inb_msg[0].mcback(port, msg_rx_ring.dev_id, -1, -1); + + /* Ack the queueing interrupt */ + out_be32((void *)&msg_regs->isr, RIO_MSG_ISR_DIQI); + } + + out: + return IRQ_HANDLED; +} + +/** + * rio_open_inb_mbox - Initialize MPC85xx inbound mailbox + * @mport: Master port implementing the inbound message unit + * @dev_id: Device specific pointer to pass on event + * @mbox: Mailbox to open + * @entries: Number of entries in the inbound mailbox ring + * + * Initializes buffer ring, request the inbound message interrupt, + * and enables the inbound message unit. Returns %0 on success + * and %-EINVAL or %-ENOMEM on failure. + */ +int rio_open_inb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries) +{ + int i, rc = 0; + + if ((entries < RIO_MIN_RX_RING_SIZE) || + (entries > RIO_MAX_RX_RING_SIZE) || (!is_power_of_2(entries))) { + rc = -EINVAL; + goto out; + } + + /* Initialize client buffer ring */ + msg_rx_ring.dev_id = dev_id; + msg_rx_ring.size = entries; + msg_rx_ring.rx_slot = 0; + for (i = 0; i < msg_rx_ring.size; i++) + msg_rx_ring.virt_buffer[i] = NULL; + + /* Initialize inbound message ring */ + if (!(msg_rx_ring.virt = dma_alloc_coherent(NULL, + msg_rx_ring.size * + RIO_MAX_MSG_SIZE, + &msg_rx_ring.phys, + GFP_KERNEL))) { + rc = -ENOMEM; + goto out; + } + + /* Point dequeue/enqueue pointers at first entry in ring */ + out_be32((void *)&msg_regs->ifqdpar, (u32) msg_rx_ring.phys); + out_be32((void *)&msg_regs->ifqepar, (u32) msg_rx_ring.phys); + + /* Clear interrupt status */ + out_be32((void *)&msg_regs->isr, 0x00000091); + + /* Hook up inbound message handler */ + if ((rc = + request_irq(MPC85xx_IRQ_RIO_RX, mpc85xx_rio_rx_handler, 0, + "msg_rx", (void *)mport)) < 0) { + dma_free_coherent(NULL, RIO_MSG_BUFFER_SIZE, + msg_tx_ring.virt_buffer[i], + msg_tx_ring.phys_buffer[i]); + goto out; + } + + /* + * Configure inbound message unit: + * Snooping + * 4KB max message size + * Unmask all interrupt sources + * Disable + */ + out_be32((void *)&msg_regs->imr, 0x001b0060); + + /* Set number of queue entries */ + out_be32((void *)&msg_regs->imr, + in_be32((void *)&msg_regs->imr) | + ((get_bitmask_order(entries) - 2) << 12)); + + /* Now enable the unit */ + out_be32((void *)&msg_regs->imr, in_be32((void *)&msg_regs->imr) | 0x1); + + out: + return rc; +} + +/** + * rio_close_inb_mbox - Shut down MPC85xx inbound mailbox + * @mport: Master port implementing the inbound message unit + * @mbox: Mailbox to close + * + * Disables the inbound message unit, free all buffers, and + * frees the inbound message interrupt. + */ +void rio_close_inb_mbox(struct rio_mport *mport, int mbox) +{ + /* Disable inbound message unit */ + out_be32((void *)&msg_regs->imr, 0); + + /* Free ring */ + dma_free_coherent(NULL, msg_rx_ring.size * RIO_MAX_MSG_SIZE, + msg_rx_ring.virt, msg_rx_ring.phys); + + /* Free interrupt */ + free_irq(MPC85xx_IRQ_RIO_RX, (void *)mport); +} + +/** + * rio_hw_add_inb_buffer - Add buffer to the MPC85xx inbound message queue + * @mport: Master port implementing the inbound message unit + * @mbox: Inbound mailbox number + * @buf: Buffer to add to inbound queue + * + * Adds the @buf buffer to the MPC85xx inbound message queue. Returns + * %0 on success or %-EINVAL on failure. + */ +int rio_hw_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf) +{ + int rc = 0; + + pr_debug("RIO: rio_hw_add_inb_buffer(), msg_rx_ring.rx_slot %d\n", + msg_rx_ring.rx_slot); + + if (msg_rx_ring.virt_buffer[msg_rx_ring.rx_slot]) { + printk(KERN_ERR + "RIO: error adding inbound buffer %d, buffer exists\n", + msg_rx_ring.rx_slot); + rc = -EINVAL; + goto out; + } + + msg_rx_ring.virt_buffer[msg_rx_ring.rx_slot] = buf; + if (++msg_rx_ring.rx_slot == msg_rx_ring.size) + msg_rx_ring.rx_slot = 0; + + out: + return rc; +} + +EXPORT_SYMBOL_GPL(rio_hw_add_inb_buffer); + +/** + * rio_hw_get_inb_message - Fetch inbound message from the MPC85xx message unit + * @mport: Master port implementing the inbound message unit + * @mbox: Inbound mailbox number + * + * Gets the next available inbound message from the inbound message queue. + * A pointer to the message is returned on success or NULL on failure. + */ +void *rio_hw_get_inb_message(struct rio_mport *mport, int mbox) +{ + u32 imr; + u32 phys_buf, virt_buf; + void *buf = NULL; + int buf_idx; + + phys_buf = in_be32((void *)&msg_regs->ifqdpar); + + /* If no more messages, then bail out */ + if (phys_buf == in_be32((void *)&msg_regs->ifqepar)) + goto out2; + + virt_buf = (u32) msg_rx_ring.virt + (phys_buf - msg_rx_ring.phys); + buf_idx = (phys_buf - msg_rx_ring.phys) / RIO_MAX_MSG_SIZE; + buf = msg_rx_ring.virt_buffer[buf_idx]; + + if (!buf) { + printk(KERN_ERR + "RIO: inbound message copy failed, no buffers\n"); + goto out1; + } + + /* Copy max message size, caller is expected to allocate that big */ + memcpy(buf, (void *)virt_buf, RIO_MAX_MSG_SIZE); + + /* Clear the available buffer */ + msg_rx_ring.virt_buffer[buf_idx] = NULL; + + out1: + imr = in_be32((void *)&msg_regs->imr); + out_be32((void *)&msg_regs->imr, imr | RIO_MSG_IMR_MI); + + out2: + return buf; +} + +EXPORT_SYMBOL_GPL(rio_hw_get_inb_message); + +/** + * mpc85xx_rio_dbell_handler - MPC85xx doorbell interrupt handler + * @irq: Linux interrupt number + * @dev_instance: Pointer to interrupt-specific data + * + * Handles doorbell interrupts. Parses a list of registered + * doorbell event handlers and executes a matching event handler. + */ +static irqreturn_t +mpc85xx_rio_dbell_handler(int irq, void *dev_instance) +{ + int dsr; + struct rio_mport *port = (struct rio_mport *)dev_instance; + + dsr = in_be32((void *)&msg_regs->dsr); + + if (dsr & DOORBELL_DSR_TE) { + pr_info("RIO: doorbell reception error\n"); + out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_TE); + goto out; + } + + if (dsr & DOORBELL_DSR_QFI) { + pr_info("RIO: doorbell queue full\n"); + out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_QFI); + goto out; + } + + /* XXX Need to check/dispatch until queue empty */ + if (dsr & DOORBELL_DSR_DIQI) { + u32 dmsg = + (u32) dbell_ring.virt + + (in_be32((void *)&msg_regs->dqdpar) & 0xfff); + u32 dmr; + struct rio_dbell *dbell; + int found = 0; + + pr_debug + ("RIO: processing doorbell, sid %2.2x tid %2.2x info %4.4x\n", + DBELL_SID(dmsg), DBELL_TID(dmsg), DBELL_INF(dmsg)); + + list_for_each_entry(dbell, &port->dbells, node) { + if ((dbell->res->start <= DBELL_INF(dmsg)) && + (dbell->res->end >= DBELL_INF(dmsg))) { + found = 1; + break; + } + } + if (found) { + dbell->dinb(port, dbell->dev_id, DBELL_SID(dmsg), DBELL_TID(dmsg), + DBELL_INF(dmsg)); + } else { + pr_debug + ("RIO: spurious doorbell, sid %2.2x tid %2.2x info %4.4x\n", + DBELL_SID(dmsg), DBELL_TID(dmsg), DBELL_INF(dmsg)); + } + dmr = in_be32((void *)&msg_regs->dmr); + out_be32((void *)&msg_regs->dmr, dmr | DOORBELL_DMR_DI); + out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_DIQI); + } + + out: + return IRQ_HANDLED; +} + +/** + * mpc85xx_rio_doorbell_init - MPC85xx doorbell interface init + * @mport: Master port implementing the inbound doorbell unit + * + * Initializes doorbell unit hardware and inbound DMA buffer + * ring. Called from mpc85xx_rio_setup(). Returns %0 on success + * or %-ENOMEM on failure. + */ +static int mpc85xx_rio_doorbell_init(struct rio_mport *mport) +{ + int rc = 0; + + /* Map outbound doorbell window immediately after maintenance window */ + if (!(dbell_win = + (u32) ioremap(mport->iores.start + RIO_MAINT_WIN_SIZE, + RIO_DBELL_WIN_SIZE))) { + printk(KERN_ERR + "RIO: unable to map outbound doorbell window\n"); + rc = -ENOMEM; + goto out; + } + + /* Initialize inbound doorbells */ + if (!(dbell_ring.virt = dma_alloc_coherent(NULL, + 512 * DOORBELL_MESSAGE_SIZE, + &dbell_ring.phys, + GFP_KERNEL))) { + printk(KERN_ERR "RIO: unable allocate inbound doorbell ring\n"); + rc = -ENOMEM; + iounmap((void *)dbell_win); + goto out; + } + + /* Point dequeue/enqueue pointers at first entry in ring */ + out_be32((void *)&msg_regs->dqdpar, (u32) dbell_ring.phys); + out_be32((void *)&msg_regs->dqepar, (u32) dbell_ring.phys); + + /* Clear interrupt status */ + out_be32((void *)&msg_regs->dsr, 0x00000091); + + /* Hook up doorbell handler */ + if ((rc = + request_irq(MPC85xx_IRQ_RIO_BELL, mpc85xx_rio_dbell_handler, 0, + "dbell_rx", (void *)mport) < 0)) { + iounmap((void *)dbell_win); + dma_free_coherent(NULL, 512 * DOORBELL_MESSAGE_SIZE, + dbell_ring.virt, dbell_ring.phys); + printk(KERN_ERR + "MPC85xx RIO: unable to request inbound doorbell irq"); + goto out; + } + + /* Configure doorbells for snooping, 512 entries, and enable */ + out_be32((void *)&msg_regs->dmr, 0x00108161); + + out: + return rc; +} + +static char *cmdline = NULL; + +static int mpc85xx_rio_get_hdid(int index) +{ + /* XXX Need to parse multiple entries in some format */ + if (!cmdline) + return -1; + + return simple_strtol(cmdline, NULL, 0); +} + +static int mpc85xx_rio_get_cmdline(char *s) +{ + if (!s) + return 0; + + cmdline = s; + return 1; +} + +__setup("riohdid=", mpc85xx_rio_get_cmdline); + +/** + * mpc85xx_rio_setup - Setup MPC85xx RapidIO interface + * @law_start: Starting physical address of RapidIO LAW + * @law_size: Size of RapidIO LAW + * + * Initializes MPC85xx RapidIO hardware interface, configures + * master port with system-specific info, and registers the + * master port with the RapidIO subsystem. + */ +void mpc85xx_rio_setup(int law_start, int law_size) +{ + struct rio_ops *ops; + struct rio_mport *port; + + ops = kmalloc(sizeof(struct rio_ops), GFP_KERNEL); + ops->lcread = mpc85xx_local_config_read; + ops->lcwrite = mpc85xx_local_config_write; + ops->cread = mpc85xx_rio_config_read; + ops->cwrite = mpc85xx_rio_config_write; + ops->dsend = mpc85xx_rio_doorbell_send; + + port = kmalloc(sizeof(struct rio_mport), GFP_KERNEL); + port->id = 0; + port->index = 0; + INIT_LIST_HEAD(&port->dbells); + port->iores.start = law_start; + port->iores.end = law_start + law_size; + port->iores.flags = IORESOURCE_MEM; + + rio_init_dbell_res(&port->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff); + rio_init_mbox_res(&port->riores[RIO_INB_MBOX_RESOURCE], 0, 0); + rio_init_mbox_res(&port->riores[RIO_OUTB_MBOX_RESOURCE], 0, 0); + strcpy(port->name, "RIO0 mport"); + + port->ops = ops; + port->host_deviceid = mpc85xx_rio_get_hdid(port->id); + + rio_register_mport(port); + + regs_win = (u32) ioremap(RIO_REGS_BASE, 0x20000); + atmu_regs = (struct rio_atmu_regs *)(regs_win + RIO_ATMU_REGS_OFFSET); + maint_atmu_regs = atmu_regs + 1; + dbell_atmu_regs = atmu_regs + 2; + msg_regs = (struct rio_msg_regs *)(regs_win + RIO_MSG_REGS_OFFSET); + + /* Configure maintenance transaction window */ + out_be32((void *)&maint_atmu_regs->rowbar, 0x000c0000); + out_be32((void *)&maint_atmu_regs->rowar, 0x80077015); + + maint_win = (u32) ioremap(law_start, RIO_MAINT_WIN_SIZE); + + /* Configure outbound doorbell window */ + out_be32((void *)&dbell_atmu_regs->rowbar, 0x000c0400); + out_be32((void *)&dbell_atmu_regs->rowar, 0x8004200b); + mpc85xx_rio_doorbell_init(port); +} diff --git a/arch/powerpc/sysdev/fsl_rio.h b/arch/powerpc/sysdev/fsl_rio.h new file mode 100644 index 00000000000..6d3ff30b157 --- /dev/null +++ b/arch/powerpc/sysdev/fsl_rio.h @@ -0,0 +1,20 @@ +/* + * MPC85xx RapidIO definitions + * + * Copyright 2005 MontaVista Software, Inc. + * Matt Porter <mporter@kernel.crashing.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PPC_SYSLIB_PPC85XX_RIO_H +#define __PPC_SYSLIB_PPC85XX_RIO_H + +#include <linux/init.h> + +extern void mpc85xx_rio_setup(int law_start, int law_size); + +#endif /* __PPC_SYSLIB_PPC85XX_RIO_H */ diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index 3ace7474809..e48b20e934c 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -24,6 +24,7 @@ #include <linux/platform_device.h> #include <linux/of_platform.h> #include <linux/phy.h> +#include <linux/phy_fixed.h> #include <linux/spi/spi.h> #include <linux/fsl_devices.h> #include <linux/fs_enet_pd.h> @@ -54,10 +55,18 @@ phys_addr_t get_immrbase(void) soc = of_find_node_by_type(NULL, "soc"); if (soc) { int size; - const void *prop = of_get_property(soc, "reg", &size); + u32 naddr; + const u32 *prop = of_get_property(soc, "#address-cells", &size); + if (prop && size == 4) + naddr = *prop; + else + naddr = 2; + + prop = of_get_property(soc, "ranges", &size); if (prop) - immrbase = of_translate_address(soc, prop); + immrbase = of_translate_address(soc, prop + naddr); + of_node_put(soc); } @@ -66,7 +75,7 @@ phys_addr_t get_immrbase(void) EXPORT_SYMBOL(get_immrbase); -#if defined(CONFIG_CPM2) || defined(CONFIG_8xx) +#if defined(CONFIG_CPM2) || defined(CONFIG_QUICC_ENGINE) || defined(CONFIG_8xx) static u32 brgfreq = -1; @@ -91,11 +100,21 @@ u32 get_brgfreq(void) /* Legacy device binding -- will go away when no users are left. */ node = of_find_node_by_type(NULL, "cpm"); + if (!node) + node = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!node) + node = of_find_node_by_type(NULL, "qe"); + if (node) { prop = of_get_property(node, "brg-frequency", &size); if (prop && size == 4) brgfreq = *prop; + if (brgfreq == -1 || brgfreq == 0) { + prop = of_get_property(node, "bus-frequency", &size); + if (prop && size == 4) + brgfreq = *prop / 2; + } of_node_put(node); } @@ -130,17 +149,51 @@ u32 get_baudrate(void) EXPORT_SYMBOL(get_baudrate); #endif /* CONFIG_CPM2 */ -static int __init gfar_mdio_of_init(void) +#ifdef CONFIG_FIXED_PHY +static int __init of_add_fixed_phys(void) { + int ret; struct device_node *np; - unsigned int i; + u32 *fixed_link; + struct fixed_phy_status status = {}; + + for_each_node_by_name(np, "ethernet") { + fixed_link = (u32 *)of_get_property(np, "fixed-link", NULL); + if (!fixed_link) + continue; + + status.link = 1; + status.duplex = fixed_link[1]; + status.speed = fixed_link[2]; + status.pause = fixed_link[3]; + status.asym_pause = fixed_link[4]; + + ret = fixed_phy_add(PHY_POLL, fixed_link[0], &status); + if (ret) { + of_node_put(np); + return ret; + } + } + + return 0; +} +arch_initcall(of_add_fixed_phys); +#endif /* CONFIG_FIXED_PHY */ + +static int __init gfar_mdio_of_init(void) +{ + struct device_node *np = NULL; struct platform_device *mdio_dev; struct resource res; int ret; - for (np = NULL, i = 0; - (np = of_find_compatible_node(np, "mdio", "gianfar")) != NULL; - i++) { + np = of_find_compatible_node(np, NULL, "fsl,gianfar-mdio"); + + /* try the deprecated version */ + if (!np) + np = of_find_compatible_node(np, "mdio", "gianfar"); + + if (np) { int k; struct device_node *child = NULL; struct gianfar_mdio_data mdio_data; @@ -179,11 +232,13 @@ static int __init gfar_mdio_of_init(void) goto unreg; } + of_node_put(np); return 0; unreg: platform_device_unregister(mdio_dev); err: + of_node_put(np); return ret; } @@ -193,7 +248,6 @@ static const char *gfar_tx_intr = "tx"; static const char *gfar_rx_intr = "rx"; static const char *gfar_err_intr = "error"; - static int __init gfar_of_init(void) { struct device_node *np; @@ -277,29 +331,43 @@ static int __init gfar_of_init(void) gfar_data.interface = PHY_INTERFACE_MODE_MII; ph = of_get_property(np, "phy-handle", NULL); - phy = of_find_node_by_phandle(*ph); + if (ph == NULL) { + u32 *fixed_link; - if (phy == NULL) { - ret = -ENODEV; - goto unreg; - } + fixed_link = (u32 *)of_get_property(np, "fixed-link", + NULL); + if (!fixed_link) { + ret = -ENODEV; + goto unreg; + } - mdio = of_get_parent(phy); + gfar_data.bus_id = 0; + gfar_data.phy_id = fixed_link[0]; + } else { + phy = of_find_node_by_phandle(*ph); + + if (phy == NULL) { + ret = -ENODEV; + goto unreg; + } + + mdio = of_get_parent(phy); + + id = of_get_property(phy, "reg", NULL); + ret = of_address_to_resource(mdio, 0, &res); + if (ret) { + of_node_put(phy); + of_node_put(mdio); + goto unreg; + } + + gfar_data.phy_id = *id; + gfar_data.bus_id = res.start; - id = of_get_property(phy, "reg", NULL); - ret = of_address_to_resource(mdio, 0, &res); - if (ret) { of_node_put(phy); of_node_put(mdio); - goto unreg; } - gfar_data.phy_id = *id; - gfar_data.bus_id = res.start; - - of_node_put(phy); - of_node_put(mdio); - ret = platform_device_add_data(gfar_dev, &gfar_data, sizeof(struct @@ -390,13 +458,11 @@ static void __init of_register_i2c_devices(struct device_node *adap_node, static int __init fsl_i2c_of_init(void) { struct device_node *np; - unsigned int i; + unsigned int i = 0; struct platform_device *i2c_dev; int ret; - for (np = NULL, i = 0; - (np = of_find_compatible_node(np, "i2c", "fsl-i2c")) != NULL; - i++) { + for_each_compatible_node(np, NULL, "fsl-i2c") { struct resource r[2]; struct fsl_i2c_platform_data i2c_data; const unsigned char *flags = NULL; @@ -432,7 +498,7 @@ static int __init fsl_i2c_of_init(void) if (ret) goto unreg; - of_register_i2c_devices(np, i); + of_register_i2c_devices(np, i++); } return 0; @@ -528,14 +594,12 @@ static enum fsl_usb2_phy_modes determine_usb_phy(const char *phy_type) static int __init fsl_usb_of_init(void) { struct device_node *np; - unsigned int i; + unsigned int i = 0; struct platform_device *usb_dev_mph = NULL, *usb_dev_dr_host = NULL, *usb_dev_dr_client = NULL; int ret; - for (np = NULL, i = 0; - (np = of_find_compatible_node(np, "usb", "fsl-usb2-mph")) != NULL; - i++) { + for_each_compatible_node(np, NULL, "fsl-usb2-mph") { struct resource r[2]; struct fsl_usb2_platform_data usb_data; const unsigned char *prop = NULL; @@ -578,11 +642,10 @@ static int __init fsl_usb_of_init(void) fsl_usb2_platform_data)); if (ret) goto unreg_mph; + i++; } - for (np = NULL; - (np = of_find_compatible_node(np, "usb", "fsl-usb2-dr")) != NULL; - i++) { + for_each_compatible_node(np, NULL, "fsl-usb2-dr") { struct resource r[2]; struct fsl_usb2_platform_data usb_data; const unsigned char *prop = NULL; @@ -654,6 +717,7 @@ static int __init fsl_usb_of_init(void) fsl_usb2_platform_data)))) goto unreg_dr; } + i++; } return 0; @@ -1125,13 +1189,12 @@ arch_initcall(fs_enet_of_init); static int __init fsl_pcmcia_of_init(void) { - struct device_node *np = NULL; + struct device_node *np; /* * Register all the devices which type is "pcmcia" */ - while ((np = of_find_compatible_node(np, - "pcmcia", "fsl,pq-pcmcia")) != NULL) - of_platform_device_create(np, "m8xx-pcmcia", NULL); + for_each_compatible_node(np, "pcmcia", "fsl,pq-pcmcia") + of_platform_device_create(np, "m8xx-pcmcia", NULL); return 0; } @@ -1213,31 +1276,17 @@ arch_initcall(cpm_smc_uart_of_init); #endif /* CONFIG_8xx */ #endif /* CONFIG_PPC_CPM_NEW_BINDING */ -int __init fsl_spi_init(struct spi_board_info *board_infos, - unsigned int num_board_infos, - void (*activate_cs)(u8 cs, u8 polarity), - void (*deactivate_cs)(u8 cs, u8 polarity)) +static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk, + struct spi_board_info *board_infos, + unsigned int num_board_infos, + void (*activate_cs)(u8 cs, u8 polarity), + void (*deactivate_cs)(u8 cs, u8 polarity)) { struct device_node *np; - unsigned int i; - const u32 *sysclk; - - /* SPI controller is either clocked from QE or SoC clock */ - np = of_find_node_by_type(NULL, "qe"); - if (!np) - np = of_find_node_by_type(NULL, "soc"); - - if (!np) - return -ENODEV; - - sysclk = of_get_property(np, "bus-frequency", NULL); - if (!sysclk) - return -ENODEV; + unsigned int i = 0; - for (np = NULL, i = 1; - (np = of_find_compatible_node(np, "spi", "fsl_spi")) != NULL; - i++) { - int ret = 0; + for_each_compatible_node(np, type, compatible) { + int ret; unsigned int j; const void *prop; struct resource res[2]; @@ -1249,13 +1298,17 @@ int __init fsl_spi_init(struct spi_board_info *board_infos, memset(res, 0, sizeof(res)); - pdata.sysclk = *sysclk; + pdata.sysclk = sysclk; prop = of_get_property(np, "reg", NULL); if (!prop) goto err; pdata.bus_num = *(u32 *)prop; + prop = of_get_property(np, "cell-index", NULL); + if (prop) + i = *(u32 *)prop; + prop = of_get_property(np, "mode", NULL); if (prop && !strcmp(prop, "cpu-qe")) pdata.qe_mode = 1; @@ -1266,7 +1319,7 @@ int __init fsl_spi_init(struct spi_board_info *board_infos, } if (!pdata.max_chipselect) - goto err; + continue; ret = of_address_to_resource(np, 0, &res[0]); if (ret) @@ -1293,13 +1346,58 @@ int __init fsl_spi_init(struct spi_board_info *board_infos, if (ret) goto unreg; - continue; + goto next; unreg: platform_device_del(pdev); err: - continue; + pr_err("%s: registration failed\n", np->full_name); +next: + i++; + } + + return i; +} + +int __init fsl_spi_init(struct spi_board_info *board_infos, + unsigned int num_board_infos, + void (*activate_cs)(u8 cs, u8 polarity), + void (*deactivate_cs)(u8 cs, u8 polarity)) +{ + u32 sysclk = -1; + int ret; + +#ifdef CONFIG_QUICC_ENGINE + /* SPI controller is either clocked from QE or SoC clock */ + sysclk = get_brgfreq(); +#endif + if (sysclk == -1) { + struct device_node *np; + const u32 *freq; + int size; + + np = of_find_node_by_type(NULL, "soc"); + if (!np) + return -ENODEV; + + freq = of_get_property(np, "clock-frequency", &size); + if (!freq || size != sizeof(*freq) || *freq == 0) { + freq = of_get_property(np, "bus-frequency", &size); + if (!freq || size != sizeof(*freq) || *freq == 0) { + of_node_put(np); + return -ENODEV; + } + } + + sysclk = *freq; + of_node_put(np); } + ret = of_fsl_spi_probe(NULL, "fsl,spi", sysclk, board_infos, + num_board_infos, activate_cs, deactivate_cs); + if (!ret) + of_fsl_spi_probe("spi", "fsl_spi", sysclk, board_infos, + num_board_infos, activate_cs, deactivate_cs); + return spi_register_board_info(board_infos, num_board_infos); } diff --git a/arch/powerpc/sysdev/grackle.c b/arch/powerpc/sysdev/grackle.c index 11ad5622eb7..d502927644c 100644 --- a/arch/powerpc/sysdev/grackle.c +++ b/arch/powerpc/sysdev/grackle.c @@ -57,7 +57,7 @@ void __init setup_grackle(struct pci_controller *hose) { setup_indirect_pci(hose, 0xfec00000, 0xfee00000, 0); if (machine_is_compatible("PowerMac1,1")) - pci_assign_all_buses = 1; + ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS; if (machine_is_compatible("AAPL,PowerBook1998")) grackle_set_loop_snoop(hose, 1); #if 0 /* Disabled for now, HW problems ??? */ diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c index e898ff4d2b9..ae0dbf4c1d6 100644 --- a/arch/powerpc/sysdev/ipic.c +++ b/arch/powerpc/sysdev/ipic.c @@ -30,11 +30,67 @@ #include "ipic.h" static struct ipic * primary_ipic; +static struct irq_chip ipic_level_irq_chip, ipic_edge_irq_chip; static DEFINE_SPINLOCK(ipic_lock); static struct ipic_info ipic_info[] = { + [1] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 16, + .prio_mask = 0, + }, + [2] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 17, + .prio_mask = 1, + }, + [3] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 18, + .prio_mask = 2, + }, + [4] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 19, + .prio_mask = 3, + }, + [5] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 20, + .prio_mask = 4, + }, + [6] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 21, + .prio_mask = 5, + }, + [7] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 22, + .prio_mask = 6, + }, + [8] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 23, + .prio_mask = 7, + }, [9] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -42,7 +98,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 0, }, [10] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -50,15 +105,27 @@ static struct ipic_info ipic_info[] = { .prio_mask = 1, }, [11] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, .bit = 26, .prio_mask = 2, }, + [12] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 27, + .prio_mask = 3, + }, + [13] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 28, + .prio_mask = 4, + }, [14] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -66,7 +133,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 5, }, [15] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -74,7 +140,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 6, }, [16] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -82,7 +147,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 7, }, [17] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_A, .force = IPIC_SEFCR, @@ -90,7 +155,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 5, }, [18] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_A, .force = IPIC_SEFCR, @@ -98,7 +163,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 6, }, [19] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_A, .force = IPIC_SEFCR, @@ -106,7 +171,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 7, }, [20] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_B, .force = IPIC_SEFCR, @@ -114,7 +179,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 4, }, [21] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_B, .force = IPIC_SEFCR, @@ -122,7 +187,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 5, }, [22] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_B, .force = IPIC_SEFCR, @@ -130,7 +195,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 6, }, [23] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_B, .force = IPIC_SEFCR, @@ -138,7 +203,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 7, }, [32] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -146,7 +210,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 0, }, [33] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -154,7 +217,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 1, }, [34] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -162,7 +224,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 2, }, [35] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -170,7 +231,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 3, }, [36] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -178,7 +238,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 4, }, [37] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -186,7 +245,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 5, }, [38] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -194,15 +252,69 @@ static struct ipic_info ipic_info[] = { .prio_mask = 6, }, [39] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, .bit = 7, .prio_mask = 7, }, + [40] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 8, + .prio_mask = 0, + }, + [41] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 9, + .prio_mask = 1, + }, + [42] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 10, + .prio_mask = 2, + }, + [43] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 11, + .prio_mask = 3, + }, + [44] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 12, + .prio_mask = 4, + }, + [45] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 13, + .prio_mask = 5, + }, + [46] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 14, + .prio_mask = 6, + }, + [47] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 15, + .prio_mask = 7, + }, [48] = { - .pend = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_A, .force = IPIC_SEFCR, @@ -210,7 +322,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 4, }, [64] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_A, .force = IPIC_SIFCR_L, @@ -218,7 +329,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 0, }, [65] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_A, .force = IPIC_SIFCR_L, @@ -226,7 +336,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 1, }, [66] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_A, .force = IPIC_SIFCR_L, @@ -234,7 +343,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 2, }, [67] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_A, .force = IPIC_SIFCR_L, @@ -242,7 +350,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 3, }, [68] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_B, .force = IPIC_SIFCR_L, @@ -250,7 +357,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 0, }, [69] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_B, .force = IPIC_SIFCR_L, @@ -258,7 +364,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 1, }, [70] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_B, .force = IPIC_SIFCR_L, @@ -266,7 +371,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 2, }, [71] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_B, .force = IPIC_SIFCR_L, @@ -274,96 +378,131 @@ static struct ipic_info ipic_info[] = { .prio_mask = 3, }, [72] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 8, }, [73] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 9, }, [74] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 10, }, [75] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 11, }, [76] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 12, }, [77] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 13, }, [78] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 14, }, [79] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 15, }, [80] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 16, }, + [81] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 17, + }, + [82] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 18, + }, + [83] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 19, + }, [84] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 20, }, [85] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 21, }, + [86] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 22, + }, + [87] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 23, + }, + [88] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 24, + }, + [89] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 25, + }, [90] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 26, }, [91] = { - .pend = IPIC_SIPNR_L, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 27, }, + [94] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 30, + }, }; static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg) @@ -412,6 +551,10 @@ static void ipic_mask_irq(unsigned int virq) temp &= ~(1 << (31 - ipic_info[src].bit)); ipic_write(ipic->regs, ipic_info[src].mask, temp); + /* mb() can't guarantee that masking is finished. But it does finish + * for nearly all cases. */ + mb(); + spin_unlock_irqrestore(&ipic_lock, flags); } @@ -424,9 +567,13 @@ static void ipic_ack_irq(unsigned int virq) spin_lock_irqsave(&ipic_lock, flags); - temp = ipic_read(ipic->regs, ipic_info[src].pend); + temp = ipic_read(ipic->regs, ipic_info[src].ack); temp |= (1 << (31 - ipic_info[src].bit)); - ipic_write(ipic->regs, ipic_info[src].pend, temp); + ipic_write(ipic->regs, ipic_info[src].ack, temp); + + /* mb() can't guarantee that ack is finished. But it does finish + * for nearly all cases. */ + mb(); spin_unlock_irqrestore(&ipic_lock, flags); } @@ -444,9 +591,13 @@ static void ipic_mask_irq_and_ack(unsigned int virq) temp &= ~(1 << (31 - ipic_info[src].bit)); ipic_write(ipic->regs, ipic_info[src].mask, temp); - temp = ipic_read(ipic->regs, ipic_info[src].pend); + temp = ipic_read(ipic->regs, ipic_info[src].ack); temp |= (1 << (31 - ipic_info[src].bit)); - ipic_write(ipic->regs, ipic_info[src].pend, temp); + ipic_write(ipic->regs, ipic_info[src].ack, temp); + + /* mb() can't guarantee that ack is finished. But it does finish + * for nearly all cases. */ + mb(); spin_unlock_irqrestore(&ipic_lock, flags); } @@ -468,14 +619,22 @@ static int ipic_set_irq_type(unsigned int virq, unsigned int flow_type) flow_type); return -EINVAL; } + /* ipic supports only edge mode on external interrupts */ + if ((flow_type & IRQ_TYPE_EDGE_FALLING) && !ipic_info[src].ack) { + printk(KERN_ERR "ipic: edge sense not supported on internal " + "interrupts\n"); + return -EINVAL; + } desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; if (flow_type & IRQ_TYPE_LEVEL_LOW) { desc->status |= IRQ_LEVEL; desc->handle_irq = handle_level_irq; + desc->chip = &ipic_level_irq_chip; } else { desc->handle_irq = handle_edge_irq; + desc->chip = &ipic_edge_irq_chip; } /* only EXT IRQ senses are programmable on ipic @@ -500,7 +659,16 @@ static int ipic_set_irq_type(unsigned int virq, unsigned int flow_type) return 0; } -static struct irq_chip ipic_irq_chip = { +/* level interrupts and edge interrupts have different ack operations */ +static struct irq_chip ipic_level_irq_chip = { + .typename = " IPIC ", + .unmask = ipic_unmask_irq, + .mask = ipic_mask_irq, + .mask_ack = ipic_mask_irq, + .set_type = ipic_set_irq_type, +}; + +static struct irq_chip ipic_edge_irq_chip = { .typename = " IPIC ", .unmask = ipic_unmask_irq, .mask = ipic_mask_irq, @@ -519,13 +687,9 @@ static int ipic_host_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hw) { struct ipic *ipic = h->host_data; - struct irq_chip *chip; - - /* Default chip */ - chip = &ipic->hc_irq; set_irq_chip_data(virq, ipic); - set_irq_chip_and_handler(virq, chip, handle_level_irq); + set_irq_chip_and_handler(virq, &ipic_level_irq_chip, handle_level_irq); /* Set default irq type */ set_irq_type(virq, IRQ_TYPE_NONE); @@ -584,7 +748,6 @@ struct ipic * __init ipic_init(struct device_node *node, unsigned int flags) ipic->regs = ioremap(res.start, res.end - res.start + 1); ipic->irqhost->host_data = ipic; - ipic->hc_irq = ipic_irq_chip; /* init hw */ ipic_write(ipic->regs, IPIC_SICNR, 0x0); @@ -593,6 +756,10 @@ struct ipic * __init ipic_init(struct device_node *node, unsigned int flags) * configure SICFR accordingly */ if (flags & IPIC_SPREADMODE_GRP_A) temp |= SICFR_IPSA; + if (flags & IPIC_SPREADMODE_GRP_B) + temp |= SICFR_IPSB; + if (flags & IPIC_SPREADMODE_GRP_C) + temp |= SICFR_IPSC; if (flags & IPIC_SPREADMODE_GRP_D) temp |= SICFR_IPSD; if (flags & IPIC_SPREADMODE_MIX_A) @@ -600,7 +767,7 @@ struct ipic * __init ipic_init(struct device_node *node, unsigned int flags) if (flags & IPIC_SPREADMODE_MIX_B) temp |= SICFR_MPSB; - ipic_write(ipic->regs, IPIC_SICNR, temp); + ipic_write(ipic->regs, IPIC_SICFR, temp); /* handle MCP route */ temp = 0; @@ -672,10 +839,12 @@ void ipic_set_highest_priority(unsigned int virq) void ipic_set_default_priority(void) { - ipic_write(primary_ipic->regs, IPIC_SIPRR_A, IPIC_SIPRR_A_DEFAULT); - ipic_write(primary_ipic->regs, IPIC_SIPRR_D, IPIC_SIPRR_D_DEFAULT); - ipic_write(primary_ipic->regs, IPIC_SMPRR_A, IPIC_SMPRR_A_DEFAULT); - ipic_write(primary_ipic->regs, IPIC_SMPRR_B, IPIC_SMPRR_B_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SIPRR_A, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SIPRR_B, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SIPRR_C, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SIPRR_D, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SMPRR_A, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SMPRR_B, IPIC_PRIORITY_DEFAULT); } void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq) diff --git a/arch/powerpc/sysdev/ipic.h b/arch/powerpc/sysdev/ipic.h index bb309a501b2..9391c57b0c5 100644 --- a/arch/powerpc/sysdev/ipic.h +++ b/arch/powerpc/sysdev/ipic.h @@ -23,13 +23,12 @@ #define IPIC_IRQ_EXT7 23 /* Default Priority Registers */ -#define IPIC_SIPRR_A_DEFAULT 0x05309770 -#define IPIC_SIPRR_D_DEFAULT 0x05309770 -#define IPIC_SMPRR_A_DEFAULT 0x05309770 -#define IPIC_SMPRR_B_DEFAULT 0x05309770 +#define IPIC_PRIORITY_DEFAULT 0x05309770 /* System Global Interrupt Configuration Register */ #define SICFR_IPSA 0x00010000 +#define SICFR_IPSB 0x00020000 +#define SICFR_IPSC 0x00040000 #define SICFR_IPSD 0x00080000 #define SICFR_MPSA 0x00200000 #define SICFR_MPSB 0x00400000 @@ -45,13 +44,11 @@ struct ipic { /* The remapper for this IPIC */ struct irq_host *irqhost; - - /* The "linux" controller struct */ - struct irq_chip hc_irq; }; struct ipic_info { - u8 pend; /* pending register offset from base */ + u8 ack; /* pending register offset from base if the irq + supports ack operation */ u8 mask; /* mask register offset from base */ u8 prio; /* priority register offset from base */ u8 force; /* force register offset from base */ diff --git a/arch/powerpc/sysdev/micropatch.c b/arch/powerpc/sysdev/micropatch.c index 712b10a55f8..d8d60284075 100644 --- a/arch/powerpc/sysdev/micropatch.c +++ b/arch/powerpc/sysdev/micropatch.c @@ -16,7 +16,7 @@ #include <asm/page.h> #include <asm/pgtable.h> #include <asm/8xx_immap.h> -#include <asm/commproc.h> +#include <asm/cpm1.h> /* * I2C/SPI relocation patch arrays. diff --git a/arch/powerpc/sysdev/mmio_nvram.c b/arch/powerpc/sysdev/mmio_nvram.c index e073e246293..7b49633a4bd 100644 --- a/arch/powerpc/sysdev/mmio_nvram.c +++ b/arch/powerpc/sysdev/mmio_nvram.c @@ -99,7 +99,7 @@ int __init mmio_nvram_init(void) nvram_addr = r.start; mmio_nvram_len = r.end - r.start + 1; if ( (!mmio_nvram_len) || (!nvram_addr) ) { - printk(KERN_WARNING "nvram: address or lenght is 0\n"); + printk(KERN_WARNING "nvram: address or length is 0\n"); ret = -EIO; goto out; } diff --git a/arch/powerpc/sysdev/mpc8xx_pic.c b/arch/powerpc/sysdev/mpc8xx_pic.c index 7aa4ff5f5ec..0e74a4bd982 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.c +++ b/arch/powerpc/sysdev/mpc8xx_pic.c @@ -10,7 +10,6 @@ #include <asm/irq.h> #include <asm/io.h> #include <asm/8xx_immap.h> -#include <asm/mpc8xx.h> #include "mpc8xx_pic.h" diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 212a94f5d34..6ffdda244bb 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -83,6 +83,7 @@ static u32 mpic_infos[][MPIC_IDX_END] = { MPIC_CPU_WHOAMI, MPIC_CPU_INTACK, MPIC_CPU_EOI, + MPIC_CPU_MCACK, MPIC_IRQ_BASE, MPIC_IRQ_STRIDE, @@ -121,6 +122,7 @@ static u32 mpic_infos[][MPIC_IDX_END] = { TSI108_CPU_WHOAMI, TSI108_CPU_INTACK, TSI108_CPU_EOI, + TSI108_CPU_MCACK, TSI108_IRQ_BASE, TSI108_IRQ_STRIDE, @@ -265,7 +267,7 @@ static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, */ -static void _mpic_map_mmio(struct mpic *mpic, unsigned long phys_addr, +static void _mpic_map_mmio(struct mpic *mpic, phys_addr_t phys_addr, struct mpic_reg_bank *rb, unsigned int offset, unsigned int size) { @@ -285,7 +287,7 @@ static void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb, BUG_ON(!DCR_MAP_OK(rb->dhost)); } -static inline void mpic_map(struct mpic *mpic, unsigned long phys_addr, +static inline void mpic_map(struct mpic *mpic, phys_addr_t phys_addr, struct mpic_reg_bank *rb, unsigned int offset, unsigned int size) { @@ -612,12 +614,11 @@ static inline void mpic_eoi(struct mpic *mpic) } #ifdef CONFIG_SMP -static irqreturn_t mpic_ipi_action(int irq, void *dev_id) +static irqreturn_t mpic_ipi_action(int irq, void *data) { - struct mpic *mpic; + long ipi = (long)data; - mpic = mpic_find(irq, NULL); - smp_message_recv(mpic_irq_to_hw(irq) - mpic->ipi_vecs[0]); + smp_message_recv(ipi); return IRQ_HANDLED; } @@ -842,6 +843,24 @@ int mpic_set_irq_type(unsigned int virq, unsigned int flow_type) return 0; } +void mpic_set_vector(unsigned int virq, unsigned int vector) +{ + struct mpic *mpic = mpic_from_irq(virq); + unsigned int src = mpic_irq_to_hw(virq); + unsigned int vecpri; + + DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n", + mpic, virq, src, vector); + + if (src >= mpic->irq_count) + return; + + vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); + vecpri = vecpri & ~MPIC_INFO(VECPRI_VECTOR_MASK); + vecpri |= vector; + mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); +} + static struct irq_chip mpic_irq_chip = { .mask = mpic_mask_irq, .unmask = mpic_unmask_irq, @@ -1109,6 +1128,11 @@ struct mpic * __init mpic_alloc(struct device_node *node, mb(); } + if (flags & MPIC_ENABLE_MCK) + mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), + mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) + | MPIC_GREG_GCONF_MCK); + /* Read feature register, calculate num CPUs and, for non-ISU * MPICs, num sources as well. On ISU MPICs, sources are counted * as ISUs are added @@ -1230,6 +1254,8 @@ void __init mpic_init(struct mpic *mpic) mpic_u3msi_init(mpic); } + mpic_pasemi_msi_init(mpic); + for (i = 0; i < mpic->num_sources; i++) { /* start with vector = source number, and masked */ u32 vecpri = MPIC_VECPRI_MASK | i | @@ -1253,6 +1279,11 @@ void __init mpic_init(struct mpic *mpic) mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) | MPIC_GREG_GCONF_8259_PTHROU_DIS); + if (mpic->flags & MPIC_NO_BIAS) + mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), + mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) + | MPIC_GREG_GCONF_NO_BIAS); + /* Set current processor priority to 0 */ mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); @@ -1419,13 +1450,13 @@ void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask) mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0])); } -unsigned int mpic_get_one_irq(struct mpic *mpic) +static unsigned int _mpic_get_one_irq(struct mpic *mpic, int reg) { u32 src; - src = mpic_cpu_read(MPIC_INFO(CPU_INTACK)) & MPIC_INFO(VECPRI_VECTOR_MASK); + src = mpic_cpu_read(reg) & MPIC_INFO(VECPRI_VECTOR_MASK); #ifdef DEBUG_LOW - DBG("%s: get_one_irq(): %d\n", mpic->name, src); + DBG("%s: get_one_irq(reg 0x%x): %d\n", mpic->name, reg, src); #endif if (unlikely(src == mpic->spurious_vec)) { if (mpic->flags & MPIC_SPV_EOI) @@ -1443,6 +1474,11 @@ unsigned int mpic_get_one_irq(struct mpic *mpic) return irq_linear_revmap(mpic->irqhost, src); } +unsigned int mpic_get_one_irq(struct mpic *mpic) +{ + return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_INTACK)); +} + unsigned int mpic_get_irq(void) { struct mpic *mpic = mpic_primary; @@ -1452,12 +1488,20 @@ unsigned int mpic_get_irq(void) return mpic_get_one_irq(mpic); } +unsigned int mpic_get_mcirq(void) +{ + struct mpic *mpic = mpic_primary; + + BUG_ON(mpic == NULL); + + return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_MCACK)); +} #ifdef CONFIG_SMP void mpic_request_ipis(void) { struct mpic *mpic = mpic_primary; - int i, err; + long i, err; static char *ipi_names[] = { "IPI0 (call function)", "IPI1 (reschedule)", @@ -1472,14 +1516,14 @@ void mpic_request_ipis(void) unsigned int vipi = irq_create_mapping(mpic->irqhost, mpic->ipi_vecs[0] + i); if (vipi == NO_IRQ) { - printk(KERN_ERR "Failed to map IPI %d\n", i); + printk(KERN_ERR "Failed to map IPI %ld\n", i); break; } err = request_irq(vipi, mpic_ipi_action, IRQF_DISABLED|IRQF_PERCPU, - ipi_names[i], mpic); + ipi_names[i], (void *)i); if (err) { - printk(KERN_ERR "Request of irq %d for IPI %d failed\n", + printk(KERN_ERR "Request of irq %d for IPI %ld failed\n", vipi, i); break; } diff --git a/arch/powerpc/sysdev/mpic.h b/arch/powerpc/sysdev/mpic.h index 1cb6bd84102..fbf8a266941 100644 --- a/arch/powerpc/sysdev/mpic.h +++ b/arch/powerpc/sysdev/mpic.h @@ -17,6 +17,7 @@ extern int mpic_msi_init_allocator(struct mpic *mpic); extern irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num); extern void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num); extern int mpic_u3msi_init(struct mpic *mpic); +extern int mpic_pasemi_msi_init(struct mpic *mpic); #else static inline void mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq) @@ -28,12 +29,15 @@ static inline int mpic_u3msi_init(struct mpic *mpic) { return -1; } + +static inline int mpic_pasemi_msi_init(struct mpic *mpic) +{ + return -1; +} #endif extern int mpic_set_irq_type(unsigned int virq, unsigned int flow_type); -extern void mpic_end_irq(unsigned int irq); -extern void mpic_mask_irq(unsigned int irq); -extern void mpic_unmask_irq(unsigned int irq); +extern void mpic_set_vector(unsigned int virq, unsigned int vector); extern void mpic_set_affinity(unsigned int irq, cpumask_t cpumask); #endif /* _POWERPC_SYSDEV_MPIC_H */ diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c new file mode 100644 index 00000000000..d6bfda30ac8 --- /dev/null +++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c @@ -0,0 +1,172 @@ +/* + * Copyright 2007, Olof Johansson, PA Semi + * + * Based on arch/powerpc/sysdev/mpic_u3msi.c: + * + * Copyright 2006, Segher Boessenkool, IBM Corporation. + * Copyright 2006-2007, Michael Ellerman, IBM Corporation. + * + * 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; version 2 of the + * License. + * + */ + +#undef DEBUG + +#include <linux/irq.h> +#include <linux/bootmem.h> +#include <linux/msi.h> +#include <asm/mpic.h> +#include <asm/prom.h> +#include <asm/hw_irq.h> +#include <asm/ppc-pci.h> + +#include "mpic.h" + +/* Allocate 16 interrupts per device, to give an alignment of 16, + * since that's the size of the grouping w.r.t. affinity. If someone + * needs more than 32 MSI's down the road we'll have to rethink this, + * but it should be OK for now. + */ +#define ALLOC_CHUNK 16 + +#define PASEMI_MSI_ADDR 0xfc080000 + +/* A bit ugly, can we get this from the pci_dev somehow? */ +static struct mpic *msi_mpic; + + +static void mpic_pasemi_msi_mask_irq(unsigned int irq) +{ + pr_debug("mpic_pasemi_msi_mask_irq %d\n", irq); + mask_msi_irq(irq); + mpic_mask_irq(irq); +} + +static void mpic_pasemi_msi_unmask_irq(unsigned int irq) +{ + pr_debug("mpic_pasemi_msi_unmask_irq %d\n", irq); + mpic_unmask_irq(irq); + unmask_msi_irq(irq); +} + +static struct irq_chip mpic_pasemi_msi_chip = { + .shutdown = mpic_pasemi_msi_mask_irq, + .mask = mpic_pasemi_msi_mask_irq, + .unmask = mpic_pasemi_msi_unmask_irq, + .eoi = mpic_end_irq, + .set_type = mpic_set_irq_type, + .set_affinity = mpic_set_affinity, + .typename = "PASEMI-MSI ", +}; + +static int pasemi_msi_check_device(struct pci_dev *pdev, int nvec, int type) +{ + if (type == PCI_CAP_ID_MSIX) + pr_debug("pasemi_msi: MSI-X untested, trying anyway\n"); + + return 0; +} + +static void pasemi_msi_teardown_msi_irqs(struct pci_dev *pdev) +{ + struct msi_desc *entry; + + pr_debug("pasemi_msi_teardown_msi_irqs, pdev %p\n", pdev); + + list_for_each_entry(entry, &pdev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; + + set_irq_msi(entry->irq, NULL); + mpic_msi_free_hwirqs(msi_mpic, virq_to_hw(entry->irq), + ALLOC_CHUNK); + irq_dispose_mapping(entry->irq); + } + + return; +} + +static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) +{ + irq_hw_number_t hwirq; + unsigned int virq; + struct msi_desc *entry; + struct msi_msg msg; + u64 addr; + + pr_debug("pasemi_msi_setup_msi_irqs, pdev %p nvec %d type %d\n", + pdev, nvec, type); + + msg.address_hi = 0; + msg.address_lo = PASEMI_MSI_ADDR; + + list_for_each_entry(entry, &pdev->msi_list, list) { + /* Allocate 16 interrupts for now, since that's the grouping for + * affinity. This can be changed later if it turns out 32 is too + * few MSIs for someone, but restrictions will apply to how the + * sources can be changed independently. + */ + hwirq = mpic_msi_alloc_hwirqs(msi_mpic, ALLOC_CHUNK); + if (hwirq < 0) { + pr_debug("pasemi_msi: failed allocating hwirq\n"); + return hwirq; + } + + virq = irq_create_mapping(msi_mpic->irqhost, hwirq); + if (virq == NO_IRQ) { + pr_debug("pasemi_msi: failed mapping hwirq 0x%lx\n", hwirq); + mpic_msi_free_hwirqs(msi_mpic, hwirq, ALLOC_CHUNK); + return -ENOSPC; + } + + /* Vector on MSI is really an offset, the hardware adds + * it to the value written at the magic address. So set + * it to 0 to remain sane. + */ + mpic_set_vector(virq, 0); + + set_irq_msi(virq, entry); + set_irq_chip(virq, &mpic_pasemi_msi_chip); + set_irq_type(virq, IRQ_TYPE_EDGE_RISING); + + pr_debug("pasemi_msi: allocated virq 0x%x (hw 0x%lx) addr 0x%lx\n", + virq, hwirq, addr); + + /* Likewise, the device writes [0...511] into the target + * register to generate MSI [512...1023] + */ + msg.data = hwirq-0x200; + write_msi_msg(virq, &msg); + } + + return 0; +} + +int mpic_pasemi_msi_init(struct mpic *mpic) +{ + int rc; + + if (!mpic->irqhost->of_node || + !of_device_is_compatible(mpic->irqhost->of_node, + "pasemi,pwrficient-openpic")) + return -ENODEV; + + rc = mpic_msi_init_allocator(mpic); + if (rc) { + pr_debug("pasemi_msi: Error allocating bitmap!\n"); + return rc; + } + + pr_debug("pasemi_msi: Registering PA Semi MPIC MSI callbacks\n"); + + msi_mpic = mpic; + WARN_ON(ppc_md.setup_msi_irqs); + ppc_md.setup_msi_irqs = pasemi_msi_setup_msi_irqs; + ppc_md.teardown_msi_irqs = pasemi_msi_teardown_msi_irqs; + ppc_md.msi_check_device = pasemi_msi_check_device; + + return 0; +} diff --git a/arch/powerpc/sysdev/mv64x60_dev.c b/arch/powerpc/sysdev/mv64x60_dev.c index 4316f5a48a0..efda0028909 100644 --- a/arch/powerpc/sysdev/mv64x60_dev.c +++ b/arch/powerpc/sysdev/mv64x60_dev.c @@ -241,7 +241,7 @@ static int __init mv64x60_eth_device_setup(struct device_node *np, int id) /* only register the shared platform device the first time through */ if (id == 0 && (err = eth_register_shared_pdev(np))) - return err;; + return err; memset(r, 0, sizeof(r)); of_irq_to_resource(np, 0, &r[0]); @@ -445,22 +445,19 @@ static int __init mv64x60_device_setup(void) int id; int err; - for (id = 0; - (np = of_find_compatible_node(np, "serial", "marvell,mpsc")); id++) - if ((err = mv64x60_mpsc_device_setup(np, id))) + id = 0; + for_each_compatible_node(np, "serial", "marvell,mpsc") + if ((err = mv64x60_mpsc_device_setup(np, id++))) goto error; - for (id = 0; - (np = of_find_compatible_node(np, "network", - "marvell,mv64x60-eth")); - id++) - if ((err = mv64x60_eth_device_setup(np, id))) + id = 0; + for_each_compatible_node(np, "network", "marvell,mv64x60-eth") + if ((err = mv64x60_eth_device_setup(np, id++))) goto error; - for (id = 0; - (np = of_find_compatible_node(np, "i2c", "marvell,mv64x60-i2c")); - id++) - if ((err = mv64x60_i2c_device_setup(np, id))) + id = 0; + for_each_compatible_node(np, "i2c", "marvell,mv64x60-i2c") + if ((err = mv64x60_i2c_device_setup(np, id++))) goto error; /* support up to one watchdog timer */ @@ -471,7 +468,6 @@ static int __init mv64x60_device_setup(void) of_node_put(np); } - return 0; error: diff --git a/arch/powerpc/sysdev/mv64x60_pci.c b/arch/powerpc/sysdev/mv64x60_pci.c index 6933f9c73b4..d21ab8fa499 100644 --- a/arch/powerpc/sysdev/mv64x60_pci.c +++ b/arch/powerpc/sysdev/mv64x60_pci.c @@ -164,8 +164,8 @@ static int __init mv64x60_add_bridge(struct device_node *dev) void __init mv64x60_pci_init(void) { - struct device_node *np = NULL; + struct device_node *np; - while ((np = of_find_compatible_node(np, "pci", "marvell,mv64x60-pci"))) + for_each_compatible_node(np, "pci", "marvell,mv64x60-pci") mv64x60_add_bridge(np); } diff --git a/arch/powerpc/sysdev/mv64x60_udbg.c b/arch/powerpc/sysdev/mv64x60_udbg.c index 367e7b13ec0..35c77c7d061 100644 --- a/arch/powerpc/sysdev/mv64x60_udbg.c +++ b/arch/powerpc/sysdev/mv64x60_udbg.c @@ -85,10 +85,10 @@ static void mv64x60_udbg_init(void) if (!stdout) return; - for (np = NULL; - (np = of_find_compatible_node(np, "serial", "marvell,mpsc")); ) + for_each_compatible_node(np, "serial", "marvell,mpsc") { if (np == stdout) break; + } of_node_put(stdout); if (!np) diff --git a/arch/powerpc/sysdev/of_rtc.c b/arch/powerpc/sysdev/of_rtc.c new file mode 100644 index 00000000000..3d54450640c --- /dev/null +++ b/arch/powerpc/sysdev/of_rtc.c @@ -0,0 +1,59 @@ +/* + * Instantiate mmio-mapped RTC chips based on device tree information + * + * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation. + * + * 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/kernel.h> +#include <linux/of.h> +#include <linux/init.h> +#include <linux/of_platform.h> + +static __initdata struct { + const char *compatible; + char *plat_name; +} of_rtc_table[] = { + { "ds1743-nvram", "rtc-ds1742" }, +}; + +void __init of_instantiate_rtc(void) +{ + struct device_node *node; + int err; + int i; + + for (i = 0; i < ARRAY_SIZE(of_rtc_table); i++) { + char *plat_name = of_rtc_table[i].plat_name; + + for_each_compatible_node(node, NULL, + of_rtc_table[i].compatible) { + struct resource *res; + + res = kmalloc(sizeof(*res), GFP_KERNEL); + if (!res) { + printk(KERN_ERR "OF RTC: Out of memory " + "allocating resource structure for %s\n", + node->full_name); + continue; + } + + err = of_address_to_resource(node, 0, res); + if (err) { + printk(KERN_ERR "OF RTC: Error " + "translating resources for %s\n", + node->full_name); + continue; + } + + printk(KERN_INFO "OF_RTC: %s is a %s @ 0x%llx-0x%llx\n", + node->full_name, plat_name, + (unsigned long long)res->start, + (unsigned long long)res->end); + platform_device_register_simple(plat_name, -1, res, 1); + } + } +} diff --git a/arch/powerpc/sysdev/pmi.c b/arch/powerpc/sysdev/pmi.c index 20edd1e94ef..c858749263e 100644 --- a/arch/powerpc/sysdev/pmi.c +++ b/arch/powerpc/sysdev/pmi.c @@ -28,9 +28,9 @@ #include <linux/completion.h> #include <linux/spinlock.h> #include <linux/workqueue.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> -#include <asm/of_device.h> -#include <asm/of_platform.h> #include <asm/io.h> #include <asm/pmi.h> #include <asm/prom.h> diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c new file mode 100644 index 00000000000..5abfcd15748 --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -0,0 +1,1528 @@ +/* + * PCI / PCI-X / PCI-Express support for 4xx parts + * + * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. + * + * Most PCI Express code is coming from Stefan Roese implementation for + * arch/ppc in the Denx tree, slightly reworked by me. + * + * Copyright 2007 DENX Software Engineering, Stefan Roese <sr@denx.de> + * + * Some of that comes itself from a previous implementation for 440SPE only + * by Roland Dreier: + * + * Copyright (c) 2005 Cisco Systems. All rights reserved. + * Roland Dreier <rolandd@cisco.com> + * + */ + +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/bootmem.h> +#include <linux/delay.h> + +#include <asm/io.h> +#include <asm/pci-bridge.h> +#include <asm/machdep.h> +#include <asm/dcr.h> +#include <asm/dcr-regs.h> + +#include "ppc4xx_pci.h" + +static int dma_offset_set; + +/* Move that to a useable header */ +extern unsigned long total_memory; + +#define U64_TO_U32_LOW(val) ((u32)((val) & 0x00000000ffffffffULL)) +#define U64_TO_U32_HIGH(val) ((u32)((val) >> 32)) + +#ifdef CONFIG_RESOURCES_64BIT +#define RES_TO_U32_LOW(val) U64_TO_U32_LOW(val) +#define RES_TO_U32_HIGH(val) U64_TO_U32_HIGH(val) +#else +#define RES_TO_U32_LOW(val) (val) +#define RES_TO_U32_HIGH(val) (0) +#endif + +static inline int ppc440spe_revA(void) +{ + /* Catch both 440SPe variants, with and without RAID6 support */ + if ((mfspr(SPRN_PVR) & 0xffefffff) == 0x53421890) + return 1; + else + return 0; +} + +static void fixup_ppc4xx_pci_bridge(struct pci_dev *dev) +{ + struct pci_controller *hose; + int i; + + if (dev->devfn != 0 || dev->bus->self != NULL) + return; + + hose = pci_bus_to_host(dev->bus); + if (hose == NULL) + return; + + if (!of_device_is_compatible(hose->dn, "ibm,plb-pciex") && + !of_device_is_compatible(hose->dn, "ibm,plb-pcix") && + !of_device_is_compatible(hose->dn, "ibm,plb-pci")) + return; + + /* Hide the PCI host BARs from the kernel as their content doesn't + * fit well in the resource management + */ + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + } + + printk(KERN_INFO "PCI: Hiding 4xx host bridge resources %s\n", + pci_name(dev)); +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, fixup_ppc4xx_pci_bridge); + +static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose, + void __iomem *reg, + struct resource *res) +{ + u64 size; + const u32 *ranges; + int rlen; + int pna = of_n_addr_cells(hose->dn); + int np = pna + 5; + + /* Default */ + res->start = 0; + res->end = size = 0x80000000; + res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; + + /* Get dma-ranges property */ + ranges = of_get_property(hose->dn, "dma-ranges", &rlen); + if (ranges == NULL) + goto out; + + /* Walk it */ + while ((rlen -= np * 4) >= 0) { + u32 pci_space = ranges[0]; + u64 pci_addr = of_read_number(ranges + 1, 2); + u64 cpu_addr = of_translate_dma_address(hose->dn, ranges + 3); + size = of_read_number(ranges + pna + 3, 2); + ranges += np; + if (cpu_addr == OF_BAD_ADDR || size == 0) + continue; + + /* We only care about memory */ + if ((pci_space & 0x03000000) != 0x02000000) + continue; + + /* We currently only support memory at 0, and pci_addr + * within 32 bits space + */ + if (cpu_addr != 0 || pci_addr > 0xffffffff) { + printk(KERN_WARNING "%s: Ignored unsupported dma range" + " 0x%016llx...0x%016llx -> 0x%016llx\n", + hose->dn->full_name, + pci_addr, pci_addr + size - 1, cpu_addr); + continue; + } + + /* Check if not prefetchable */ + if (!(pci_space & 0x40000000)) + res->flags &= ~IORESOURCE_PREFETCH; + + + /* Use that */ + res->start = pci_addr; +#ifndef CONFIG_RESOURCES_64BIT + /* Beware of 32 bits resources */ + if ((pci_addr + size) > 0x100000000ull) + res->end = 0xffffffff; + else +#endif + res->end = res->start + size - 1; + break; + } + + /* We only support one global DMA offset */ + if (dma_offset_set && pci_dram_offset != res->start) { + printk(KERN_ERR "%s: dma-ranges(s) mismatch\n", + hose->dn->full_name); + return -ENXIO; + } + + /* Check that we can fit all of memory as we don't support + * DMA bounce buffers + */ + if (size < total_memory) { + printk(KERN_ERR "%s: dma-ranges too small " + "(size=%llx total_memory=%lx)\n", + hose->dn->full_name, size, total_memory); + return -ENXIO; + } + + /* Check we are a power of 2 size and that base is a multiple of size*/ + if (!is_power_of_2(size) || + (res->start & (size - 1)) != 0) { + printk(KERN_ERR "%s: dma-ranges unaligned\n", + hose->dn->full_name); + return -ENXIO; + } + + /* Check that we are fully contained within 32 bits space */ + if (res->end > 0xffffffff) { + printk(KERN_ERR "%s: dma-ranges outside of 32 bits space\n", + hose->dn->full_name); + return -ENXIO; + } + out: + dma_offset_set = 1; + pci_dram_offset = res->start; + + printk(KERN_INFO "4xx PCI DMA offset set to 0x%08lx\n", + pci_dram_offset); + return 0; +} + +/* + * 4xx PCI 2.x part + */ + +static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose, + void __iomem *reg) +{ + u32 la, ma, pcila, pciha; + int i, j; + + /* Setup outbound memory windows */ + for (i = j = 0; i < 3; i++) { + struct resource *res = &hose->mem_resources[i]; + + /* we only care about memory windows */ + if (!(res->flags & IORESOURCE_MEM)) + continue; + if (j > 2) { + printk(KERN_WARNING "%s: Too many ranges\n", + hose->dn->full_name); + break; + } + + /* Calculate register values */ + la = res->start; + pciha = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset); + pcila = RES_TO_U32_LOW(res->start - hose->pci_mem_offset); + + ma = res->end + 1 - res->start; + if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) { + printk(KERN_WARNING "%s: Resource out of range\n", + hose->dn->full_name); + continue; + } + ma = (0xffffffffu << ilog2(ma)) | 0x1; + if (res->flags & IORESOURCE_PREFETCH) + ma |= 0x2; + + /* Program register values */ + writel(la, reg + PCIL0_PMM0LA + (0x10 * j)); + writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * j)); + writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * j)); + writel(ma, reg + PCIL0_PMM0MA + (0x10 * j)); + j++; + } +} + +static void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose, + void __iomem *reg, + const struct resource *res) +{ + resource_size_t size = res->end - res->start + 1; + u32 sa; + + /* Calculate window size */ + sa = (0xffffffffu << ilog2(size)) | 1; + sa |= 0x1; + + /* RAM is always at 0 local for now */ + writel(0, reg + PCIL0_PTM1LA); + writel(sa, reg + PCIL0_PTM1MS); + + /* Map on PCI side */ + early_write_config_dword(hose, hose->first_busno, 0, + PCI_BASE_ADDRESS_1, res->start); + early_write_config_dword(hose, hose->first_busno, 0, + PCI_BASE_ADDRESS_2, 0x00000000); + early_write_config_word(hose, hose->first_busno, 0, + PCI_COMMAND, 0x0006); +} + +static void __init ppc4xx_probe_pci_bridge(struct device_node *np) +{ + /* NYI */ + struct resource rsrc_cfg; + struct resource rsrc_reg; + struct resource dma_window; + struct pci_controller *hose = NULL; + void __iomem *reg = NULL; + const int *bus_range; + int primary = 0; + + /* Fetch config space registers address */ + if (of_address_to_resource(np, 0, &rsrc_cfg)) { + printk(KERN_ERR "%s:Can't get PCI config register base !", + np->full_name); + return; + } + /* Fetch host bridge internal registers address */ + if (of_address_to_resource(np, 3, &rsrc_reg)) { + printk(KERN_ERR "%s: Can't get PCI internal register base !", + np->full_name); + return; + } + + /* Check if primary bridge */ + if (of_get_property(np, "primary", NULL)) + primary = 1; + + /* Get bus range if any */ + bus_range = of_get_property(np, "bus-range", NULL); + + /* Map registers */ + reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start); + if (reg == NULL) { + printk(KERN_ERR "%s: Can't map registers !", np->full_name); + goto fail; + } + + /* Allocate the host controller data structure */ + hose = pcibios_alloc_controller(np); + if (!hose) + goto fail; + + hose->first_busno = bus_range ? bus_range[0] : 0x0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + /* Setup config space */ + setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0); + + /* Disable all windows */ + writel(0, reg + PCIL0_PMM0MA); + writel(0, reg + PCIL0_PMM1MA); + writel(0, reg + PCIL0_PMM2MA); + writel(0, reg + PCIL0_PTM1MS); + writel(0, reg + PCIL0_PTM2MS); + + /* Parse outbound mapping resources */ + pci_process_bridge_OF_ranges(hose, np, primary); + + /* Parse inbound mapping resources */ + if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0) + goto fail; + + /* Configure outbound ranges POMs */ + ppc4xx_configure_pci_PMMs(hose, reg); + + /* Configure inbound ranges PIMs */ + ppc4xx_configure_pci_PTMs(hose, reg, &dma_window); + + /* We don't need the registers anymore */ + iounmap(reg); + return; + + fail: + if (hose) + pcibios_free_controller(hose); + if (reg) + iounmap(reg); +} + +/* + * 4xx PCI-X part + */ + +static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose, + void __iomem *reg) +{ + u32 lah, lal, pciah, pcial, sa; + int i, j; + + /* Setup outbound memory windows */ + for (i = j = 0; i < 3; i++) { + struct resource *res = &hose->mem_resources[i]; + + /* we only care about memory windows */ + if (!(res->flags & IORESOURCE_MEM)) + continue; + if (j > 1) { + printk(KERN_WARNING "%s: Too many ranges\n", + hose->dn->full_name); + break; + } + + /* Calculate register values */ + lah = RES_TO_U32_HIGH(res->start); + lal = RES_TO_U32_LOW(res->start); + pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset); + pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset); + sa = res->end + 1 - res->start; + if (!is_power_of_2(sa) || sa < 0x100000 || + sa > 0xffffffffu) { + printk(KERN_WARNING "%s: Resource out of range\n", + hose->dn->full_name); + continue; + } + sa = (0xffffffffu << ilog2(sa)) | 0x1; + + /* Program register values */ + if (j == 0) { + writel(lah, reg + PCIX0_POM0LAH); + writel(lal, reg + PCIX0_POM0LAL); + writel(pciah, reg + PCIX0_POM0PCIAH); + writel(pcial, reg + PCIX0_POM0PCIAL); + writel(sa, reg + PCIX0_POM0SA); + } else { + writel(lah, reg + PCIX0_POM1LAH); + writel(lal, reg + PCIX0_POM1LAL); + writel(pciah, reg + PCIX0_POM1PCIAH); + writel(pcial, reg + PCIX0_POM1PCIAL); + writel(sa, reg + PCIX0_POM1SA); + } + j++; + } +} + +static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose, + void __iomem *reg, + const struct resource *res, + int big_pim, + int enable_msi_hole) +{ + resource_size_t size = res->end - res->start + 1; + u32 sa; + + /* RAM is always at 0 */ + writel(0x00000000, reg + PCIX0_PIM0LAH); + writel(0x00000000, reg + PCIX0_PIM0LAL); + + /* Calculate window size */ + sa = (0xffffffffu << ilog2(size)) | 1; + sa |= 0x1; + if (res->flags & IORESOURCE_PREFETCH) + sa |= 0x2; + if (enable_msi_hole) + sa |= 0x4; + writel(sa, reg + PCIX0_PIM0SA); + if (big_pim) + writel(0xffffffff, reg + PCIX0_PIM0SAH); + + /* Map on PCI side */ + writel(0x00000000, reg + PCIX0_BAR0H); + writel(res->start, reg + PCIX0_BAR0L); + writew(0x0006, reg + PCIX0_COMMAND); +} + +static void __init ppc4xx_probe_pcix_bridge(struct device_node *np) +{ + struct resource rsrc_cfg; + struct resource rsrc_reg; + struct resource dma_window; + struct pci_controller *hose = NULL; + void __iomem *reg = NULL; + const int *bus_range; + int big_pim = 0, msi = 0, primary = 0; + + /* Fetch config space registers address */ + if (of_address_to_resource(np, 0, &rsrc_cfg)) { + printk(KERN_ERR "%s:Can't get PCI-X config register base !", + np->full_name); + return; + } + /* Fetch host bridge internal registers address */ + if (of_address_to_resource(np, 3, &rsrc_reg)) { + printk(KERN_ERR "%s: Can't get PCI-X internal register base !", + np->full_name); + return; + } + + /* Check if it supports large PIMs (440GX) */ + if (of_get_property(np, "large-inbound-windows", NULL)) + big_pim = 1; + + /* Check if we should enable MSIs inbound hole */ + if (of_get_property(np, "enable-msi-hole", NULL)) + msi = 1; + + /* Check if primary bridge */ + if (of_get_property(np, "primary", NULL)) + primary = 1; + + /* Get bus range if any */ + bus_range = of_get_property(np, "bus-range", NULL); + + /* Map registers */ + reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start); + if (reg == NULL) { + printk(KERN_ERR "%s: Can't map registers !", np->full_name); + goto fail; + } + + /* Allocate the host controller data structure */ + hose = pcibios_alloc_controller(np); + if (!hose) + goto fail; + + hose->first_busno = bus_range ? bus_range[0] : 0x0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + /* Setup config space */ + setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0); + + /* Disable all windows */ + writel(0, reg + PCIX0_POM0SA); + writel(0, reg + PCIX0_POM1SA); + writel(0, reg + PCIX0_POM2SA); + writel(0, reg + PCIX0_PIM0SA); + writel(0, reg + PCIX0_PIM1SA); + writel(0, reg + PCIX0_PIM2SA); + if (big_pim) { + writel(0, reg + PCIX0_PIM0SAH); + writel(0, reg + PCIX0_PIM2SAH); + } + + /* Parse outbound mapping resources */ + pci_process_bridge_OF_ranges(hose, np, primary); + + /* Parse inbound mapping resources */ + if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0) + goto fail; + + /* Configure outbound ranges POMs */ + ppc4xx_configure_pcix_POMs(hose, reg); + + /* Configure inbound ranges PIMs */ + ppc4xx_configure_pcix_PIMs(hose, reg, &dma_window, big_pim, msi); + + /* We don't need the registers anymore */ + iounmap(reg); + return; + + fail: + if (hose) + pcibios_free_controller(hose); + if (reg) + iounmap(reg); +} + +#ifdef CONFIG_PPC4xx_PCI_EXPRESS + +/* + * 4xx PCI-Express part + * + * We support 3 parts currently based on the compatible property: + * + * ibm,plb-pciex-440spe + * ibm,plb-pciex-405ex + * + * Anything else will be rejected for now as they are all subtly + * different unfortunately. + * + */ + +#define MAX_PCIE_BUS_MAPPED 0x40 + +struct ppc4xx_pciex_port +{ + struct pci_controller *hose; + struct device_node *node; + unsigned int index; + int endpoint; + int link; + int has_ibpre; + unsigned int sdr_base; + dcr_host_t dcrs; + struct resource cfg_space; + struct resource utl_regs; + void __iomem *utl_base; +}; + +static struct ppc4xx_pciex_port *ppc4xx_pciex_ports; +static unsigned int ppc4xx_pciex_port_count; + +struct ppc4xx_pciex_hwops +{ + int (*core_init)(struct device_node *np); + int (*port_init_hw)(struct ppc4xx_pciex_port *port); + int (*setup_utl)(struct ppc4xx_pciex_port *port); +}; + +static struct ppc4xx_pciex_hwops *ppc4xx_pciex_hwops; + +#ifdef CONFIG_44x + +/* Check various reset bits of the 440SPe PCIe core */ +static int __init ppc440spe_pciex_check_reset(struct device_node *np) +{ + u32 valPE0, valPE1, valPE2; + int err = 0; + + /* SDR0_PEGPLLLCT1 reset */ + if (!(mfdcri(SDR0, PESDR0_PLLLCT1) & 0x01000000)) { + /* + * the PCIe core was probably already initialised + * by firmware - let's re-reset RCSSET regs + * + * -- Shouldn't we also re-reset the whole thing ? -- BenH + */ + pr_debug("PCIE: SDR0_PLLLCT1 already reset.\n"); + mtdcri(SDR0, PESDR0_440SPE_RCSSET, 0x01010000); + mtdcri(SDR0, PESDR1_440SPE_RCSSET, 0x01010000); + mtdcri(SDR0, PESDR2_440SPE_RCSSET, 0x01010000); + } + + valPE0 = mfdcri(SDR0, PESDR0_440SPE_RCSSET); + valPE1 = mfdcri(SDR0, PESDR1_440SPE_RCSSET); + valPE2 = mfdcri(SDR0, PESDR2_440SPE_RCSSET); + + /* SDR0_PExRCSSET rstgu */ + if (!(valPE0 & 0x01000000) || + !(valPE1 & 0x01000000) || + !(valPE2 & 0x01000000)) { + printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstgu error\n"); + err = -1; + } + + /* SDR0_PExRCSSET rstdl */ + if (!(valPE0 & 0x00010000) || + !(valPE1 & 0x00010000) || + !(valPE2 & 0x00010000)) { + printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstdl error\n"); + err = -1; + } + + /* SDR0_PExRCSSET rstpyn */ + if ((valPE0 & 0x00001000) || + (valPE1 & 0x00001000) || + (valPE2 & 0x00001000)) { + printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstpyn error\n"); + err = -1; + } + + /* SDR0_PExRCSSET hldplb */ + if ((valPE0 & 0x10000000) || + (valPE1 & 0x10000000) || + (valPE2 & 0x10000000)) { + printk(KERN_INFO "PCIE: SDR0_PExRCSSET hldplb error\n"); + err = -1; + } + + /* SDR0_PExRCSSET rdy */ + if ((valPE0 & 0x00100000) || + (valPE1 & 0x00100000) || + (valPE2 & 0x00100000)) { + printk(KERN_INFO "PCIE: SDR0_PExRCSSET rdy error\n"); + err = -1; + } + + /* SDR0_PExRCSSET shutdown */ + if ((valPE0 & 0x00000100) || + (valPE1 & 0x00000100) || + (valPE2 & 0x00000100)) { + printk(KERN_INFO "PCIE: SDR0_PExRCSSET shutdown error\n"); + err = -1; + } + + return err; +} + +/* Global PCIe core initializations for 440SPe core */ +static int __init ppc440spe_pciex_core_init(struct device_node *np) +{ + int time_out = 20; + + /* Set PLL clock receiver to LVPECL */ + mtdcri(SDR0, PESDR0_PLLLCT1, mfdcri(SDR0, PESDR0_PLLLCT1) | 1 << 28); + + /* Shouldn't we do all the calibration stuff etc... here ? */ + if (ppc440spe_pciex_check_reset(np)) + return -ENXIO; + + if (!(mfdcri(SDR0, PESDR0_PLLLCT2) & 0x10000)) { + printk(KERN_INFO "PCIE: PESDR_PLLCT2 resistance calibration " + "failed (0x%08x)\n", + mfdcri(SDR0, PESDR0_PLLLCT2)); + return -1; + } + + /* De-assert reset of PCIe PLL, wait for lock */ + mtdcri(SDR0, PESDR0_PLLLCT1, + mfdcri(SDR0, PESDR0_PLLLCT1) & ~(1 << 24)); + udelay(3); + + while (time_out) { + if (!(mfdcri(SDR0, PESDR0_PLLLCT3) & 0x10000000)) { + time_out--; + udelay(1); + } else + break; + } + if (!time_out) { + printk(KERN_INFO "PCIE: VCO output not locked\n"); + return -1; + } + + pr_debug("PCIE initialization OK\n"); + + return 3; +} + +static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +{ + u32 val = 1 << 24; + + if (port->endpoint) + val = PTYPE_LEGACY_ENDPOINT << 20; + else + val = PTYPE_ROOT_PORT << 20; + + if (port->index == 0) + val |= LNKW_X8 << 12; + else + val |= LNKW_X4 << 12; + + mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val); + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x20222222); + if (ppc440spe_revA()) + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x11000000); + mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL0SET1, 0x35000000); + mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL1SET1, 0x35000000); + mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL2SET1, 0x35000000); + mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL3SET1, 0x35000000); + if (port->index == 0) { + mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL4SET1, + 0x35000000); + mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL5SET1, + 0x35000000); + mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL6SET1, + 0x35000000); + mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL7SET1, + 0x35000000); + } + val = mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET); + mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, + (val & ~(1 << 24 | 1 << 16)) | 1 << 12); + + return 0; +} + +static int ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +{ + return ppc440spe_pciex_init_port_hw(port); +} + +static int ppc440speB_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +{ + int rc = ppc440spe_pciex_init_port_hw(port); + + port->has_ibpre = 1; + + return rc; +} + +static int ppc440speA_pciex_init_utl(struct ppc4xx_pciex_port *port) +{ + /* XXX Check what that value means... I hate magic */ + dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x68782800); + + /* + * Set buffer allocations and then assert VRB and TXE. + */ + out_be32(port->utl_base + PEUTL_OUTTR, 0x08000000); + out_be32(port->utl_base + PEUTL_INTR, 0x02000000); + out_be32(port->utl_base + PEUTL_OPDBSZ, 0x10000000); + out_be32(port->utl_base + PEUTL_PBBSZ, 0x53000000); + out_be32(port->utl_base + PEUTL_IPHBSZ, 0x08000000); + out_be32(port->utl_base + PEUTL_IPDBSZ, 0x10000000); + out_be32(port->utl_base + PEUTL_RCIRQEN, 0x00f00000); + out_be32(port->utl_base + PEUTL_PCTL, 0x80800066); + + return 0; +} + +static int ppc440speB_pciex_init_utl(struct ppc4xx_pciex_port *port) +{ + /* Report CRS to the operating system */ + out_be32(port->utl_base + PEUTL_PBCTL, 0x08000000); + + return 0; +} + +static struct ppc4xx_pciex_hwops ppc440speA_pcie_hwops __initdata = +{ + .core_init = ppc440spe_pciex_core_init, + .port_init_hw = ppc440speA_pciex_init_port_hw, + .setup_utl = ppc440speA_pciex_init_utl, +}; + +static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata = +{ + .core_init = ppc440spe_pciex_core_init, + .port_init_hw = ppc440speB_pciex_init_port_hw, + .setup_utl = ppc440speB_pciex_init_utl, +}; + +#endif /* CONFIG_44x */ + +#ifdef CONFIG_40x + +static int __init ppc405ex_pciex_core_init(struct device_node *np) +{ + /* Nothing to do, return 2 ports */ + return 2; +} + +static void ppc405ex_pcie_phy_reset(struct ppc4xx_pciex_port *port) +{ + /* Assert the PE0_PHY reset */ + mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01010000); + msleep(1); + + /* deassert the PE0_hotreset */ + if (port->endpoint) + mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01111000); + else + mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01101000); + + /* poll for phy !reset */ + /* XXX FIXME add timeout */ + while (!(mfdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSTA) & 0x00001000)) + ; + + /* deassert the PE0_gpl_utl_reset */ + mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x00101000); +} + +static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +{ + u32 val; + + if (port->endpoint) + val = PTYPE_LEGACY_ENDPOINT; + else + val = PTYPE_ROOT_PORT; + + mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, + 1 << 24 | val << 20 | LNKW_X1 << 12); + + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x00000000); + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01010000); + mtdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSET1, 0x720F0000); + mtdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSET2, 0x70600003); + + /* + * Only reset the PHY when no link is currently established. + * This is for the Atheros PCIe board which has problems to establish + * the link (again) after this PHY reset. All other currently tested + * PCIe boards don't show this problem. + * This has to be re-tested and fixed in a later release! + */ +#if 0 /* XXX FIXME: Not resetting the PHY will leave all resources + * configured as done previously by U-Boot. Then Linux will currently + * not reassign them. So the PHY reset is now done always. This will + * lead to problems with the Atheros PCIe board again. + */ + val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP); + if (!(val & 0x00001000)) + ppc405ex_pcie_phy_reset(port); +#else + ppc405ex_pcie_phy_reset(port); +#endif + + dcr_write(port->dcrs, DCRO_PEGPL_CFG, 0x10000000); /* guarded on */ + + port->has_ibpre = 1; + + return 0; +} + +static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port) +{ + dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x0); + + /* + * Set buffer allocations and then assert VRB and TXE. + */ + out_be32(port->utl_base + PEUTL_OUTTR, 0x02000000); + out_be32(port->utl_base + PEUTL_INTR, 0x02000000); + out_be32(port->utl_base + PEUTL_OPDBSZ, 0x04000000); + out_be32(port->utl_base + PEUTL_PBBSZ, 0x21000000); + out_be32(port->utl_base + PEUTL_IPHBSZ, 0x02000000); + out_be32(port->utl_base + PEUTL_IPDBSZ, 0x04000000); + out_be32(port->utl_base + PEUTL_RCIRQEN, 0x00f00000); + out_be32(port->utl_base + PEUTL_PCTL, 0x80800066); + + out_be32(port->utl_base + PEUTL_PBCTL, 0x08000000); + + return 0; +} + +static struct ppc4xx_pciex_hwops ppc405ex_pcie_hwops __initdata = +{ + .core_init = ppc405ex_pciex_core_init, + .port_init_hw = ppc405ex_pciex_init_port_hw, + .setup_utl = ppc405ex_pciex_init_utl, +}; + +#endif /* CONFIG_40x */ + + +/* Check that the core has been initied and if not, do it */ +static int __init ppc4xx_pciex_check_core_init(struct device_node *np) +{ + static int core_init; + int count = -ENODEV; + + if (core_init++) + return 0; + +#ifdef CONFIG_44x + if (of_device_is_compatible(np, "ibm,plb-pciex-440spe")) { + if (ppc440spe_revA()) + ppc4xx_pciex_hwops = &ppc440speA_pcie_hwops; + else + ppc4xx_pciex_hwops = &ppc440speB_pcie_hwops; + } +#endif /* CONFIG_44x */ +#ifdef CONFIG_40x + if (of_device_is_compatible(np, "ibm,plb-pciex-405ex")) + ppc4xx_pciex_hwops = &ppc405ex_pcie_hwops; +#endif + if (ppc4xx_pciex_hwops == NULL) { + printk(KERN_WARNING "PCIE: unknown host type %s\n", + np->full_name); + return -ENODEV; + } + + count = ppc4xx_pciex_hwops->core_init(np); + if (count > 0) { + ppc4xx_pciex_ports = + kzalloc(count * sizeof(struct ppc4xx_pciex_port), + GFP_KERNEL); + if (ppc4xx_pciex_ports) { + ppc4xx_pciex_port_count = count; + return 0; + } + printk(KERN_WARNING "PCIE: failed to allocate ports array\n"); + return -ENOMEM; + } + return -ENODEV; +} + +static void __init ppc4xx_pciex_port_init_mapping(struct ppc4xx_pciex_port *port) +{ + /* We map PCI Express configuration based on the reg property */ + dcr_write(port->dcrs, DCRO_PEGPL_CFGBAH, + RES_TO_U32_HIGH(port->cfg_space.start)); + dcr_write(port->dcrs, DCRO_PEGPL_CFGBAL, + RES_TO_U32_LOW(port->cfg_space.start)); + + /* XXX FIXME: Use size from reg property. For now, map 512M */ + dcr_write(port->dcrs, DCRO_PEGPL_CFGMSK, 0xe0000001); + + /* We map UTL registers based on the reg property */ + dcr_write(port->dcrs, DCRO_PEGPL_REGBAH, + RES_TO_U32_HIGH(port->utl_regs.start)); + dcr_write(port->dcrs, DCRO_PEGPL_REGBAL, + RES_TO_U32_LOW(port->utl_regs.start)); + + /* XXX FIXME: Use size from reg property */ + dcr_write(port->dcrs, DCRO_PEGPL_REGMSK, 0x00007001); + + /* Disable all other outbound windows */ + dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, 0); + dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, 0); + dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0); + dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0); +} + +static int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port, + unsigned int sdr_offset, + unsigned int mask, + unsigned int value, + int timeout_ms) +{ + u32 val; + + while(timeout_ms--) { + val = mfdcri(SDR0, port->sdr_base + sdr_offset); + if ((val & mask) == value) { + pr_debug("PCIE%d: Wait on SDR %x success with tm %d (%08x)\n", + port->index, sdr_offset, timeout_ms, val); + return 0; + } + msleep(1); + } + return -1; +} + +static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) +{ + int rc = 0; + + /* Init HW */ + if (ppc4xx_pciex_hwops->port_init_hw) + rc = ppc4xx_pciex_hwops->port_init_hw(port); + if (rc != 0) + return rc; + + printk(KERN_INFO "PCIE%d: Checking link...\n", + port->index); + + /* Wait for reset to complete */ + if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) { + printk(KERN_WARNING "PCIE%d: PGRST failed\n", + port->index); + return -1; + } + + /* Check for card presence detect if supported, if not, just wait for + * link unconditionally. + * + * note that we don't fail if there is no link, we just filter out + * config space accesses. That way, it will be easier to implement + * hotplug later on. + */ + if (!port->has_ibpre || + !ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP, + 1 << 28, 1 << 28, 100)) { + printk(KERN_INFO + "PCIE%d: Device detected, waiting for link...\n", + port->index); + if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP, + 0x1000, 0x1000, 2000)) + printk(KERN_WARNING + "PCIE%d: Link up failed\n", port->index); + else { + printk(KERN_INFO + "PCIE%d: link is up !\n", port->index); + port->link = 1; + } + } else + printk(KERN_INFO "PCIE%d: No device detected.\n", port->index); + + /* + * Initialize mapping: disable all regions and configure + * CFG and REG regions based on resources in the device tree + */ + ppc4xx_pciex_port_init_mapping(port); + + /* + * Map UTL + */ + port->utl_base = ioremap(port->utl_regs.start, 0x100); + BUG_ON(port->utl_base == NULL); + + /* + * Setup UTL registers --BenH. + */ + if (ppc4xx_pciex_hwops->setup_utl) + ppc4xx_pciex_hwops->setup_utl(port); + + /* + * Check for VC0 active and assert RDY. + */ + if (port->link && + ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, + 1 << 16, 1 << 16, 5000)) { + printk(KERN_INFO "PCIE%d: VC0 not active\n", port->index); + port->link = 0; + } + + mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, + mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | 1 << 20); + msleep(100); + + return 0; +} + +static int ppc4xx_pciex_validate_bdf(struct ppc4xx_pciex_port *port, + struct pci_bus *bus, + unsigned int devfn) +{ + static int message; + + /* Endpoint can not generate upstream(remote) config cycles */ + if (port->endpoint && bus->number != port->hose->first_busno) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Check we are within the mapped range */ + if (bus->number > port->hose->last_busno) { + if (!message) { + printk(KERN_WARNING "Warning! Probing bus %u" + " out of range !\n", bus->number); + message++; + } + return PCIBIOS_DEVICE_NOT_FOUND; + } + + /* The root complex has only one device / function */ + if (bus->number == port->hose->first_busno && devfn != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* The other side of the RC has only one device as well */ + if (bus->number == (port->hose->first_busno + 1) && + PCI_SLOT(devfn) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Check if we have a link */ + if ((bus->number != port->hose->first_busno) && !port->link) + return PCIBIOS_DEVICE_NOT_FOUND; + + return 0; +} + +static void __iomem *ppc4xx_pciex_get_config_base(struct ppc4xx_pciex_port *port, + struct pci_bus *bus, + unsigned int devfn) +{ + int relbus; + + /* Remove the casts when we finally remove the stupid volatile + * in struct pci_controller + */ + if (bus->number == port->hose->first_busno) + return (void __iomem *)port->hose->cfg_addr; + + relbus = bus->number - (port->hose->first_busno + 1); + return (void __iomem *)port->hose->cfg_data + + ((relbus << 20) | (devfn << 12)); +} + +static int ppc4xx_pciex_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + struct pci_controller *hose = (struct pci_controller *) bus->sysdata; + struct ppc4xx_pciex_port *port = + &ppc4xx_pciex_ports[hose->indirect_type]; + void __iomem *addr; + u32 gpl_cfg; + + BUG_ON(hose != port->hose); + + if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = ppc4xx_pciex_get_config_base(port, bus, devfn); + + /* + * Reading from configuration space of non-existing device can + * generate transaction errors. For the read duration we suppress + * assertion of machine check exceptions to avoid those. + */ + gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG); + dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA); + + /* Make sure no CRS is recorded */ + out_be32(port->utl_base + PEUTL_RCSTA, 0x00040000); + + switch (len) { + case 1: + *val = in_8((u8 *)(addr + offset)); + break; + case 2: + *val = in_le16((u16 *)(addr + offset)); + break; + default: + *val = in_le32((u32 *)(addr + offset)); + break; + } + + pr_debug("pcie-config-read: bus=%3d [%3d..%3d] devfn=0x%04x" + " offset=0x%04x len=%d, addr=0x%p val=0x%08x\n", + bus->number, hose->first_busno, hose->last_busno, + devfn, offset, len, addr + offset, *val); + + /* Check for CRS (440SPe rev B does that for us but heh ..) */ + if (in_be32(port->utl_base + PEUTL_RCSTA) & 0x00040000) { + pr_debug("Got CRS !\n"); + if (len != 4 || offset != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + *val = 0xffff0001; + } + + dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg); + + return PCIBIOS_SUCCESSFUL; +} + +static int ppc4xx_pciex_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct pci_controller *hose = (struct pci_controller *) bus->sysdata; + struct ppc4xx_pciex_port *port = + &ppc4xx_pciex_ports[hose->indirect_type]; + void __iomem *addr; + u32 gpl_cfg; + + if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = ppc4xx_pciex_get_config_base(port, bus, devfn); + + /* + * Reading from configuration space of non-existing device can + * generate transaction errors. For the read duration we suppress + * assertion of machine check exceptions to avoid those. + */ + gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG); + dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA); + + pr_debug("pcie-config-write: bus=%3d [%3d..%3d] devfn=0x%04x" + " offset=0x%04x len=%d, addr=0x%p val=0x%08x\n", + bus->number, hose->first_busno, hose->last_busno, + devfn, offset, len, addr + offset, val); + + switch (len) { + case 1: + out_8((u8 *)(addr + offset), val); + break; + case 2: + out_le16((u16 *)(addr + offset), val); + break; + default: + out_le32((u32 *)(addr + offset), val); + break; + } + + dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops ppc4xx_pciex_pci_ops = +{ + .read = ppc4xx_pciex_read_config, + .write = ppc4xx_pciex_write_config, +}; + +static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port, + struct pci_controller *hose, + void __iomem *mbase) +{ + u32 lah, lal, pciah, pcial, sa; + int i, j; + + /* Setup outbound memory windows */ + for (i = j = 0; i < 3; i++) { + struct resource *res = &hose->mem_resources[i]; + + /* we only care about memory windows */ + if (!(res->flags & IORESOURCE_MEM)) + continue; + if (j > 1) { + printk(KERN_WARNING "%s: Too many ranges\n", + port->node->full_name); + break; + } + + /* Calculate register values */ + lah = RES_TO_U32_HIGH(res->start); + lal = RES_TO_U32_LOW(res->start); + pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset); + pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset); + sa = res->end + 1 - res->start; + if (!is_power_of_2(sa) || sa < 0x100000 || + sa > 0xffffffffu) { + printk(KERN_WARNING "%s: Resource out of range\n", + port->node->full_name); + continue; + } + sa = (0xffffffffu << ilog2(sa)) | 0x1; + + /* Program register values */ + switch (j) { + case 0: + out_le32(mbase + PECFG_POM0LAH, pciah); + out_le32(mbase + PECFG_POM0LAL, pcial); + dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah); + dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal); + dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff); + dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3); + break; + case 1: + out_le32(mbase + PECFG_POM1LAH, pciah); + out_le32(mbase + PECFG_POM1LAL, pcial); + dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah); + dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal); + dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff); + dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3); + break; + } + j++; + } + + /* Configure IO, always 64K starting at 0 */ + if (hose->io_resource.flags & IORESOURCE_IO) { + lah = RES_TO_U32_HIGH(hose->io_base_phys); + lal = RES_TO_U32_LOW(hose->io_base_phys); + out_le32(mbase + PECFG_POM2LAH, 0); + out_le32(mbase + PECFG_POM2LAL, 0); + dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah); + dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal); + dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff); + dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0xffff0000 | 3); + } +} + +static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port, + struct pci_controller *hose, + void __iomem *mbase, + struct resource *res) +{ + resource_size_t size = res->end - res->start + 1; + u64 sa; + + /* Calculate window size */ + sa = (0xffffffffffffffffull << ilog2(size));; + if (res->flags & IORESOURCE_PREFETCH) + sa |= 0x8; + + out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa)); + out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa)); + + /* The setup of the split looks weird to me ... let's see if it works */ + out_le32(mbase + PECFG_PIM0LAL, 0x00000000); + out_le32(mbase + PECFG_PIM0LAH, 0x00000000); + out_le32(mbase + PECFG_PIM1LAL, 0x00000000); + out_le32(mbase + PECFG_PIM1LAH, 0x00000000); + out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); + out_le32(mbase + PECFG_PIM01SAL, 0x00000000); + + /* Enable inbound mapping */ + out_le32(mbase + PECFG_PIMEN, 0x1); + + out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(res->start)); + out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(res->start)); + + /* Enable I/O, Mem, and Busmaster cycles */ + out_le16(mbase + PCI_COMMAND, + in_le16(mbase + PCI_COMMAND) | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); +} + +static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) +{ + struct resource dma_window; + struct pci_controller *hose = NULL; + const int *bus_range; + int primary = 0, busses; + void __iomem *mbase = NULL, *cfg_data = NULL; + + /* XXX FIXME: Handle endpoint mode properly */ + if (port->endpoint) { + printk(KERN_WARNING "PCIE%d: Port in endpoint mode !\n", + port->index); + return; + } + + /* Check if primary bridge */ + if (of_get_property(port->node, "primary", NULL)) + primary = 1; + + /* Get bus range if any */ + bus_range = of_get_property(port->node, "bus-range", NULL); + + /* Allocate the host controller data structure */ + hose = pcibios_alloc_controller(port->node); + if (!hose) + goto fail; + + /* We stick the port number in "indirect_type" so the config space + * ops can retrieve the port data structure easily + */ + hose->indirect_type = port->index; + + /* Get bus range */ + hose->first_busno = bus_range ? bus_range[0] : 0x0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + /* Because of how big mapping the config space is (1M per bus), we + * limit how many busses we support. In the long run, we could replace + * that with something akin to kmap_atomic instead. We set aside 1 bus + * for the host itself too. + */ + busses = hose->last_busno - hose->first_busno; /* This is off by 1 */ + if (busses > MAX_PCIE_BUS_MAPPED) { + busses = MAX_PCIE_BUS_MAPPED; + hose->last_busno = hose->first_busno + busses; + } + + /* We map the external config space in cfg_data and the host config + * space in cfg_addr. External space is 1M per bus, internal space + * is 4K + */ + cfg_data = ioremap(port->cfg_space.start + + (hose->first_busno + 1) * 0x100000, + busses * 0x100000); + mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000); + if (cfg_data == NULL || mbase == NULL) { + printk(KERN_ERR "%s: Can't map config space !", + port->node->full_name); + goto fail; + } + + hose->cfg_data = cfg_data; + hose->cfg_addr = mbase; + + pr_debug("PCIE %s, bus %d..%d\n", port->node->full_name, + hose->first_busno, hose->last_busno); + pr_debug(" config space mapped at: root @0x%p, other @0x%p\n", + hose->cfg_addr, hose->cfg_data); + + /* Setup config space */ + hose->ops = &ppc4xx_pciex_pci_ops; + port->hose = hose; + mbase = (void __iomem *)hose->cfg_addr; + + /* + * Set bus numbers on our root port + */ + out_8(mbase + PCI_PRIMARY_BUS, hose->first_busno); + out_8(mbase + PCI_SECONDARY_BUS, hose->first_busno + 1); + out_8(mbase + PCI_SUBORDINATE_BUS, hose->last_busno); + + /* + * OMRs are already reset, also disable PIMs + */ + out_le32(mbase + PECFG_PIMEN, 0); + + /* Parse outbound mapping resources */ + pci_process_bridge_OF_ranges(hose, port->node, primary); + + /* Parse inbound mapping resources */ + if (ppc4xx_parse_dma_ranges(hose, mbase, &dma_window) != 0) + goto fail; + + /* Configure outbound ranges POMs */ + ppc4xx_configure_pciex_POMs(port, hose, mbase); + + /* Configure inbound ranges PIMs */ + ppc4xx_configure_pciex_PIMs(port, hose, mbase, &dma_window); + + /* The root complex doesn't show up if we don't set some vendor + * and device IDs into it. Those are the same bogus one that the + * initial code in arch/ppc add. We might want to change that. + */ + out_le16(mbase + 0x200, 0xaaa0 + port->index); + out_le16(mbase + 0x202, 0xbed0 + port->index); + + /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ + out_le32(mbase + 0x208, 0x06040001); + + printk(KERN_INFO "PCIE%d: successfully set as root-complex\n", + port->index); + return; + fail: + if (hose) + pcibios_free_controller(hose); + if (cfg_data) + iounmap(cfg_data); + if (mbase) + iounmap(mbase); +} + +static void __init ppc4xx_probe_pciex_bridge(struct device_node *np) +{ + struct ppc4xx_pciex_port *port; + const u32 *pval; + int portno; + unsigned int dcrs; + + /* First, proceed to core initialization as we assume there's + * only one PCIe core in the system + */ + if (ppc4xx_pciex_check_core_init(np)) + return; + + /* Get the port number from the device-tree */ + pval = of_get_property(np, "port", NULL); + if (pval == NULL) { + printk(KERN_ERR "PCIE: Can't find port number for %s\n", + np->full_name); + return; + } + portno = *pval; + if (portno >= ppc4xx_pciex_port_count) { + printk(KERN_ERR "PCIE: port number out of range for %s\n", + np->full_name); + return; + } + port = &ppc4xx_pciex_ports[portno]; + port->index = portno; + port->node = of_node_get(np); + pval = of_get_property(np, "sdr-base", NULL); + if (pval == NULL) { + printk(KERN_ERR "PCIE: missing sdr-base for %s\n", + np->full_name); + return; + } + port->sdr_base = *pval; + + /* XXX Currently, we only support root complex mode */ + port->endpoint = 0; + + /* Fetch config space registers address */ + if (of_address_to_resource(np, 0, &port->cfg_space)) { + printk(KERN_ERR "%s: Can't get PCI-E config space !", + np->full_name); + return; + } + /* Fetch host bridge internal registers address */ + if (of_address_to_resource(np, 1, &port->utl_regs)) { + printk(KERN_ERR "%s: Can't get UTL register base !", + np->full_name); + return; + } + + /* Map DCRs */ + dcrs = dcr_resource_start(np, 0); + if (dcrs == 0) { + printk(KERN_ERR "%s: Can't get DCR register base !", + np->full_name); + return; + } + port->dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0)); + + /* Initialize the port specific registers */ + if (ppc4xx_pciex_port_init(port)) { + printk(KERN_WARNING "PCIE%d: Port init failed\n", port->index); + return; + } + + /* Setup the linux hose data structure */ + ppc4xx_pciex_port_setup_hose(port); +} + +#endif /* CONFIG_PPC4xx_PCI_EXPRESS */ + +static int __init ppc4xx_pci_find_bridges(void) +{ + struct device_node *np; + +#ifdef CONFIG_PPC4xx_PCI_EXPRESS + for_each_compatible_node(np, NULL, "ibm,plb-pciex") + ppc4xx_probe_pciex_bridge(np); +#endif + for_each_compatible_node(np, NULL, "ibm,plb-pcix") + ppc4xx_probe_pcix_bridge(np); + for_each_compatible_node(np, NULL, "ibm,plb-pci") + ppc4xx_probe_pci_bridge(np); + + return 0; +} +arch_initcall(ppc4xx_pci_find_bridges); + diff --git a/arch/powerpc/sysdev/ppc4xx_pci.h b/arch/powerpc/sysdev/ppc4xx_pci.h new file mode 100644 index 00000000000..1c07908dc6e --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_pci.h @@ -0,0 +1,369 @@ +/* + * PCI / PCI-X / PCI-Express support for 4xx parts + * + * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. + * + * Bits and pieces extracted from arch/ppc support by + * + * Matt Porter <mporter@kernel.crashing.org> + * + * Copyright 2002-2005 MontaVista Software Inc. + */ +#ifndef __PPC4XX_PCI_H__ +#define __PPC4XX_PCI_H__ + +/* + * 4xx PCI-X bridge register definitions + */ +#define PCIX0_VENDID 0x000 +#define PCIX0_DEVID 0x002 +#define PCIX0_COMMAND 0x004 +#define PCIX0_STATUS 0x006 +#define PCIX0_REVID 0x008 +#define PCIX0_CLS 0x009 +#define PCIX0_CACHELS 0x00c +#define PCIX0_LATTIM 0x00d +#define PCIX0_HDTYPE 0x00e +#define PCIX0_BIST 0x00f +#define PCIX0_BAR0L 0x010 +#define PCIX0_BAR0H 0x014 +#define PCIX0_BAR1 0x018 +#define PCIX0_BAR2L 0x01c +#define PCIX0_BAR2H 0x020 +#define PCIX0_BAR3 0x024 +#define PCIX0_CISPTR 0x028 +#define PCIX0_SBSYSVID 0x02c +#define PCIX0_SBSYSID 0x02e +#define PCIX0_EROMBA 0x030 +#define PCIX0_CAP 0x034 +#define PCIX0_RES0 0x035 +#define PCIX0_RES1 0x036 +#define PCIX0_RES2 0x038 +#define PCIX0_INTLN 0x03c +#define PCIX0_INTPN 0x03d +#define PCIX0_MINGNT 0x03e +#define PCIX0_MAXLTNCY 0x03f +#define PCIX0_BRDGOPT1 0x040 +#define PCIX0_BRDGOPT2 0x044 +#define PCIX0_ERREN 0x050 +#define PCIX0_ERRSTS 0x054 +#define PCIX0_PLBBESR 0x058 +#define PCIX0_PLBBEARL 0x05c +#define PCIX0_PLBBEARH 0x060 +#define PCIX0_POM0LAL 0x068 +#define PCIX0_POM0LAH 0x06c +#define PCIX0_POM0SA 0x070 +#define PCIX0_POM0PCIAL 0x074 +#define PCIX0_POM0PCIAH 0x078 +#define PCIX0_POM1LAL 0x07c +#define PCIX0_POM1LAH 0x080 +#define PCIX0_POM1SA 0x084 +#define PCIX0_POM1PCIAL 0x088 +#define PCIX0_POM1PCIAH 0x08c +#define PCIX0_POM2SA 0x090 +#define PCIX0_PIM0SAL 0x098 +#define PCIX0_PIM0SA PCIX0_PIM0SAL +#define PCIX0_PIM0LAL 0x09c +#define PCIX0_PIM0LAH 0x0a0 +#define PCIX0_PIM1SA 0x0a4 +#define PCIX0_PIM1LAL 0x0a8 +#define PCIX0_PIM1LAH 0x0ac +#define PCIX0_PIM2SAL 0x0b0 +#define PCIX0_PIM2SA PCIX0_PIM2SAL +#define PCIX0_PIM2LAL 0x0b4 +#define PCIX0_PIM2LAH 0x0b8 +#define PCIX0_OMCAPID 0x0c0 +#define PCIX0_OMNIPTR 0x0c1 +#define PCIX0_OMMC 0x0c2 +#define PCIX0_OMMA 0x0c4 +#define PCIX0_OMMUA 0x0c8 +#define PCIX0_OMMDATA 0x0cc +#define PCIX0_OMMEOI 0x0ce +#define PCIX0_PMCAPID 0x0d0 +#define PCIX0_PMNIPTR 0x0d1 +#define PCIX0_PMC 0x0d2 +#define PCIX0_PMCSR 0x0d4 +#define PCIX0_PMCSRBSE 0x0d6 +#define PCIX0_PMDATA 0x0d7 +#define PCIX0_PMSCRR 0x0d8 +#define PCIX0_CAPID 0x0dc +#define PCIX0_NIPTR 0x0dd +#define PCIX0_CMD 0x0de +#define PCIX0_STS 0x0e0 +#define PCIX0_IDR 0x0e4 +#define PCIX0_CID 0x0e8 +#define PCIX0_RID 0x0ec +#define PCIX0_PIM0SAH 0x0f8 +#define PCIX0_PIM2SAH 0x0fc +#define PCIX0_MSGIL 0x100 +#define PCIX0_MSGIH 0x104 +#define PCIX0_MSGOL 0x108 +#define PCIX0_MSGOH 0x10c +#define PCIX0_IM 0x1f8 + +/* + * 4xx PCI bridge register definitions + */ +#define PCIL0_PMM0LA 0x00 +#define PCIL0_PMM0MA 0x04 +#define PCIL0_PMM0PCILA 0x08 +#define PCIL0_PMM0PCIHA 0x0c +#define PCIL0_PMM1LA 0x10 +#define PCIL0_PMM1MA 0x14 +#define PCIL0_PMM1PCILA 0x18 +#define PCIL0_PMM1PCIHA 0x1c +#define PCIL0_PMM2LA 0x20 +#define PCIL0_PMM2MA 0x24 +#define PCIL0_PMM2PCILA 0x28 +#define PCIL0_PMM2PCIHA 0x2c +#define PCIL0_PTM1MS 0x30 +#define PCIL0_PTM1LA 0x34 +#define PCIL0_PTM2MS 0x38 +#define PCIL0_PTM2LA 0x3c + +/* + * 4xx PCIe bridge register definitions + */ + +/* DCR offsets */ +#define DCRO_PEGPL_CFGBAH 0x00 +#define DCRO_PEGPL_CFGBAL 0x01 +#define DCRO_PEGPL_CFGMSK 0x02 +#define DCRO_PEGPL_MSGBAH 0x03 +#define DCRO_PEGPL_MSGBAL 0x04 +#define DCRO_PEGPL_MSGMSK 0x05 +#define DCRO_PEGPL_OMR1BAH 0x06 +#define DCRO_PEGPL_OMR1BAL 0x07 +#define DCRO_PEGPL_OMR1MSKH 0x08 +#define DCRO_PEGPL_OMR1MSKL 0x09 +#define DCRO_PEGPL_OMR2BAH 0x0a +#define DCRO_PEGPL_OMR2BAL 0x0b +#define DCRO_PEGPL_OMR2MSKH 0x0c +#define DCRO_PEGPL_OMR2MSKL 0x0d +#define DCRO_PEGPL_OMR3BAH 0x0e +#define DCRO_PEGPL_OMR3BAL 0x0f +#define DCRO_PEGPL_OMR3MSKH 0x10 +#define DCRO_PEGPL_OMR3MSKL 0x11 +#define DCRO_PEGPL_REGBAH 0x12 +#define DCRO_PEGPL_REGBAL 0x13 +#define DCRO_PEGPL_REGMSK 0x14 +#define DCRO_PEGPL_SPECIAL 0x15 +#define DCRO_PEGPL_CFG 0x16 +#define DCRO_PEGPL_ESR 0x17 +#define DCRO_PEGPL_EARH 0x18 +#define DCRO_PEGPL_EARL 0x19 +#define DCRO_PEGPL_EATR 0x1a + +/* DMER mask */ +#define GPL_DMER_MASK_DISA 0x02000000 + +/* + * System DCRs (SDRs) + */ +#define PESDR0_PLLLCT1 0x03a0 +#define PESDR0_PLLLCT2 0x03a1 +#define PESDR0_PLLLCT3 0x03a2 + +/* + * 440SPe additional DCRs + */ +#define PESDR0_440SPE_UTLSET1 0x0300 +#define PESDR0_440SPE_UTLSET2 0x0301 +#define PESDR0_440SPE_DLPSET 0x0302 +#define PESDR0_440SPE_LOOP 0x0303 +#define PESDR0_440SPE_RCSSET 0x0304 +#define PESDR0_440SPE_RCSSTS 0x0305 +#define PESDR0_440SPE_HSSL0SET1 0x0306 +#define PESDR0_440SPE_HSSL0SET2 0x0307 +#define PESDR0_440SPE_HSSL0STS 0x0308 +#define PESDR0_440SPE_HSSL1SET1 0x0309 +#define PESDR0_440SPE_HSSL1SET2 0x030a +#define PESDR0_440SPE_HSSL1STS 0x030b +#define PESDR0_440SPE_HSSL2SET1 0x030c +#define PESDR0_440SPE_HSSL2SET2 0x030d +#define PESDR0_440SPE_HSSL2STS 0x030e +#define PESDR0_440SPE_HSSL3SET1 0x030f +#define PESDR0_440SPE_HSSL3SET2 0x0310 +#define PESDR0_440SPE_HSSL3STS 0x0311 +#define PESDR0_440SPE_HSSL4SET1 0x0312 +#define PESDR0_440SPE_HSSL4SET2 0x0313 +#define PESDR0_440SPE_HSSL4STS 0x0314 +#define PESDR0_440SPE_HSSL5SET1 0x0315 +#define PESDR0_440SPE_HSSL5SET2 0x0316 +#define PESDR0_440SPE_HSSL5STS 0x0317 +#define PESDR0_440SPE_HSSL6SET1 0x0318 +#define PESDR0_440SPE_HSSL6SET2 0x0319 +#define PESDR0_440SPE_HSSL6STS 0x031a +#define PESDR0_440SPE_HSSL7SET1 0x031b +#define PESDR0_440SPE_HSSL7SET2 0x031c +#define PESDR0_440SPE_HSSL7STS 0x031d +#define PESDR0_440SPE_HSSCTLSET 0x031e +#define PESDR0_440SPE_LANE_ABCD 0x031f +#define PESDR0_440SPE_LANE_EFGH 0x0320 + +#define PESDR1_440SPE_UTLSET1 0x0340 +#define PESDR1_440SPE_UTLSET2 0x0341 +#define PESDR1_440SPE_DLPSET 0x0342 +#define PESDR1_440SPE_LOOP 0x0343 +#define PESDR1_440SPE_RCSSET 0x0344 +#define PESDR1_440SPE_RCSSTS 0x0345 +#define PESDR1_440SPE_HSSL0SET1 0x0346 +#define PESDR1_440SPE_HSSL0SET2 0x0347 +#define PESDR1_440SPE_HSSL0STS 0x0348 +#define PESDR1_440SPE_HSSL1SET1 0x0349 +#define PESDR1_440SPE_HSSL1SET2 0x034a +#define PESDR1_440SPE_HSSL1STS 0x034b +#define PESDR1_440SPE_HSSL2SET1 0x034c +#define PESDR1_440SPE_HSSL2SET2 0x034d +#define PESDR1_440SPE_HSSL2STS 0x034e +#define PESDR1_440SPE_HSSL3SET1 0x034f +#define PESDR1_440SPE_HSSL3SET2 0x0350 +#define PESDR1_440SPE_HSSL3STS 0x0351 +#define PESDR1_440SPE_HSSCTLSET 0x0352 +#define PESDR1_440SPE_LANE_ABCD 0x0353 + +#define PESDR2_440SPE_UTLSET1 0x0370 +#define PESDR2_440SPE_UTLSET2 0x0371 +#define PESDR2_440SPE_DLPSET 0x0372 +#define PESDR2_440SPE_LOOP 0x0373 +#define PESDR2_440SPE_RCSSET 0x0374 +#define PESDR2_440SPE_RCSSTS 0x0375 +#define PESDR2_440SPE_HSSL0SET1 0x0376 +#define PESDR2_440SPE_HSSL0SET2 0x0377 +#define PESDR2_440SPE_HSSL0STS 0x0378 +#define PESDR2_440SPE_HSSL1SET1 0x0379 +#define PESDR2_440SPE_HSSL1SET2 0x037a +#define PESDR2_440SPE_HSSL1STS 0x037b +#define PESDR2_440SPE_HSSL2SET1 0x037c +#define PESDR2_440SPE_HSSL2SET2 0x037d +#define PESDR2_440SPE_HSSL2STS 0x037e +#define PESDR2_440SPE_HSSL3SET1 0x037f +#define PESDR2_440SPE_HSSL3SET2 0x0380 +#define PESDR2_440SPE_HSSL3STS 0x0381 +#define PESDR2_440SPE_HSSCTLSET 0x0382 +#define PESDR2_440SPE_LANE_ABCD 0x0383 + +/* + * 405EX additional DCRs + */ +#define PESDR0_405EX_UTLSET1 0x0400 +#define PESDR0_405EX_UTLSET2 0x0401 +#define PESDR0_405EX_DLPSET 0x0402 +#define PESDR0_405EX_LOOP 0x0403 +#define PESDR0_405EX_RCSSET 0x0404 +#define PESDR0_405EX_RCSSTS 0x0405 +#define PESDR0_405EX_PHYSET1 0x0406 +#define PESDR0_405EX_PHYSET2 0x0407 +#define PESDR0_405EX_BIST 0x0408 +#define PESDR0_405EX_LPB 0x040B +#define PESDR0_405EX_PHYSTA 0x040C + +#define PESDR1_405EX_UTLSET1 0x0440 +#define PESDR1_405EX_UTLSET2 0x0441 +#define PESDR1_405EX_DLPSET 0x0442 +#define PESDR1_405EX_LOOP 0x0443 +#define PESDR1_405EX_RCSSET 0x0444 +#define PESDR1_405EX_RCSSTS 0x0445 +#define PESDR1_405EX_PHYSET1 0x0446 +#define PESDR1_405EX_PHYSET2 0x0447 +#define PESDR1_405EX_BIST 0x0448 +#define PESDR1_405EX_LPB 0x044B +#define PESDR1_405EX_PHYSTA 0x044C + +/* + * Of the above, some are common offsets from the base + */ +#define PESDRn_UTLSET1 0x00 +#define PESDRn_UTLSET2 0x01 +#define PESDRn_DLPSET 0x02 +#define PESDRn_LOOP 0x03 +#define PESDRn_RCSSET 0x04 +#define PESDRn_RCSSTS 0x05 + +/* 440spe only */ +#define PESDRn_440SPE_HSSL0SET1 0x06 +#define PESDRn_440SPE_HSSL0SET2 0x07 +#define PESDRn_440SPE_HSSL0STS 0x08 +#define PESDRn_440SPE_HSSL1SET1 0x09 +#define PESDRn_440SPE_HSSL1SET2 0x0a +#define PESDRn_440SPE_HSSL1STS 0x0b +#define PESDRn_440SPE_HSSL2SET1 0x0c +#define PESDRn_440SPE_HSSL2SET2 0x0d +#define PESDRn_440SPE_HSSL2STS 0x0e +#define PESDRn_440SPE_HSSL3SET1 0x0f +#define PESDRn_440SPE_HSSL3SET2 0x10 +#define PESDRn_440SPE_HSSL3STS 0x11 + +/* 440spe port 0 only */ +#define PESDRn_440SPE_HSSL4SET1 0x12 +#define PESDRn_440SPE_HSSL4SET2 0x13 +#define PESDRn_440SPE_HSSL4STS 0x14 +#define PESDRn_440SPE_HSSL5SET1 0x15 +#define PESDRn_440SPE_HSSL5SET2 0x16 +#define PESDRn_440SPE_HSSL5STS 0x17 +#define PESDRn_440SPE_HSSL6SET1 0x18 +#define PESDRn_440SPE_HSSL6SET2 0x19 +#define PESDRn_440SPE_HSSL6STS 0x1a +#define PESDRn_440SPE_HSSL7SET1 0x1b +#define PESDRn_440SPE_HSSL7SET2 0x1c +#define PESDRn_440SPE_HSSL7STS 0x1d + +/* 405ex only */ +#define PESDRn_405EX_PHYSET1 0x06 +#define PESDRn_405EX_PHYSET2 0x07 +#define PESDRn_405EX_PHYSTA 0x0c + +/* + * UTL register offsets + */ +#define PEUTL_PBCTL 0x00 +#define PEUTL_PBBSZ 0x20 +#define PEUTL_OPDBSZ 0x68 +#define PEUTL_IPHBSZ 0x70 +#define PEUTL_IPDBSZ 0x78 +#define PEUTL_OUTTR 0x90 +#define PEUTL_INTR 0x98 +#define PEUTL_PCTL 0xa0 +#define PEUTL_RCSTA 0xB0 +#define PEUTL_RCIRQEN 0xb8 + +/* + * Config space register offsets + */ +#define PECFG_ECRTCTL 0x074 + +#define PECFG_BAR0LMPA 0x210 +#define PECFG_BAR0HMPA 0x214 +#define PECFG_BAR1MPA 0x218 +#define PECFG_BAR2LMPA 0x220 +#define PECFG_BAR2HMPA 0x224 + +#define PECFG_PIMEN 0x33c +#define PECFG_PIM0LAL 0x340 +#define PECFG_PIM0LAH 0x344 +#define PECFG_PIM1LAL 0x348 +#define PECFG_PIM1LAH 0x34c +#define PECFG_PIM01SAL 0x350 +#define PECFG_PIM01SAH 0x354 + +#define PECFG_POM0LAL 0x380 +#define PECFG_POM0LAH 0x384 +#define PECFG_POM1LAL 0x388 +#define PECFG_POM1LAH 0x38c +#define PECFG_POM2LAL 0x390 +#define PECFG_POM2LAH 0x394 + + +enum +{ + PTYPE_ENDPOINT = 0x0, + PTYPE_LEGACY_ENDPOINT = 0x1, + PTYPE_ROOT_PORT = 0x4, + + LNKW_X1 = 0x1, + LNKW_X4 = 0x4, + LNKW_X8 = 0x8 +}; + + +#endif /* __PPC4XX_PCI_H__ */ diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig index f611d344a12..adc66212a41 100644 --- a/arch/powerpc/sysdev/qe_lib/Kconfig +++ b/arch/powerpc/sysdev/qe_lib/Kconfig @@ -4,7 +4,7 @@ config UCC_SLOW bool - default n + default y if SERIAL_QE help This option provides qe_lib support to UCC slow protocols: UART, BISYNC, QMC diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c index 3d57d3835b0..5ef844da935 100644 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ b/arch/powerpc/sysdev/qe_lib/qe.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/ioport.h> +#include <linux/crc32.h> #include <asm/irq.h> #include <asm/page.h> #include <asm/pgtable.h> @@ -64,17 +65,22 @@ static phys_addr_t qebase = -1; phys_addr_t get_qe_base(void) { struct device_node *qe; + unsigned int size; + const void *prop; if (qebase != -1) return qebase; - qe = of_find_node_by_type(NULL, "qe"); - if (qe) { - unsigned int size; - const void *prop = of_get_property(qe, "reg", &size); - qebase = of_translate_address(qe, prop); - of_node_put(qe); - }; + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!qe) { + qe = of_find_node_by_type(NULL, "qe"); + if (!qe) + return qebase; + } + + prop = of_get_property(qe, "reg", &size); + qebase = of_translate_address(qe, prop); + of_node_put(qe); return qebase; } @@ -152,34 +158,45 @@ static unsigned int brg_clk = 0; unsigned int get_brg_clk(void) { struct device_node *qe; + unsigned int size; + const u32 *prop; + if (brg_clk) return brg_clk; - qe = of_find_node_by_type(NULL, "qe"); - if (qe) { - unsigned int size; - const u32 *prop = of_get_property(qe, "brg-frequency", &size); - brg_clk = *prop; - of_node_put(qe); - }; + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!qe) { + qe = of_find_node_by_type(NULL, "qe"); + if (!qe) + return brg_clk; + } + + prop = of_get_property(qe, "brg-frequency", &size); + if (!prop || size != sizeof(*prop)) + return brg_clk; + + brg_clk = *prop; + of_node_put(qe); + return brg_clk; } /* Program the BRG to the given sampling rate and multiplier * - * @brg: the BRG, 1-16 + * @brg: the BRG, QE_BRG1 - QE_BRG16 * @rate: the desired sampling rate * @multiplier: corresponds to the value programmed in GUMR_L[RDCR] or * GUMR_L[TDCR]. E.g., if this BRG is the RX clock, and GUMR_L[RDCR]=01, * then 'multiplier' should be 8. - * - * Also note that the value programmed into the BRGC register must be even. */ -void qe_setbrg(unsigned int brg, unsigned int rate, unsigned int multiplier) +int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier) { u32 divisor, tempval; u32 div16 = 0; + if ((brg < QE_BRG1) || (brg > QE_BRG16)) + return -EINVAL; + divisor = get_brg_clk() / (rate * multiplier); if (divisor > QE_BRGC_DIVISOR_MAX + 1) { @@ -196,8 +213,43 @@ void qe_setbrg(unsigned int brg, unsigned int rate, unsigned int multiplier) tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE | div16; - out_be32(&qe_immr->brg.brgc[brg - 1], tempval); + out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval); + + return 0; +} +EXPORT_SYMBOL(qe_setbrg); + +/* Convert a string to a QE clock source enum + * + * This function takes a string, typically from a property in the device + * tree, and returns the corresponding "enum qe_clock" value. +*/ +enum qe_clock qe_clock_source(const char *source) +{ + unsigned int i; + + if (strcasecmp(source, "none") == 0) + return QE_CLK_NONE; + + if (strncasecmp(source, "brg", 3) == 0) { + i = simple_strtoul(source + 3, NULL, 10); + if ((i >= 1) && (i <= 16)) + return (QE_BRG1 - 1) + i; + else + return QE_CLK_DUMMY; + } + + if (strncasecmp(source, "clk", 3) == 0) { + i = simple_strtoul(source + 3, NULL, 10); + if ((i >= 1) && (i <= 24)) + return (QE_CLK1 - 1) + i; + else + return QE_CLK_DUMMY; + } + + return QE_CLK_DUMMY; } +EXPORT_SYMBOL(qe_clock_source); /* Initialize SNUMs (thread serial numbers) according to * QE Module Control chapter, SNUM table @@ -285,7 +337,7 @@ static rh_info_t qe_muram_info; static void qe_muram_init(void) { struct device_node *np; - u32 address; + const u32 *address; u64 size; unsigned int flags; @@ -298,11 +350,21 @@ static void qe_muram_init(void) /* XXX: This is a subset of the available muram. It * varies with the processor and the microcode patches activated. */ - if ((np = of_find_node_by_name(NULL, "data-only")) != NULL) { - address = *of_get_address(np, 0, &size, &flags); - of_node_put(np); - rh_attach_region(&qe_muram_info, address, (int) size); + np = of_find_compatible_node(NULL, NULL, "fsl,qe-muram-data"); + if (!np) { + np = of_find_node_by_name(NULL, "data-only"); + if (!np) { + WARN_ON(1); + return; + } } + + address = of_get_address(np, 0, &size, &flags); + WARN_ON(!address); + + of_node_put(np); + if (address) + rh_attach_region(&qe_muram_info, *address, (int)size); } /* This function returns an index into the MURAM area. @@ -358,3 +420,249 @@ void *qe_muram_addr(unsigned long offset) return (void *)&qe_immr->muram[offset]; } EXPORT_SYMBOL(qe_muram_addr); + +/* The maximum number of RISCs we support */ +#define MAX_QE_RISC 2 + +/* Firmware information stored here for qe_get_firmware_info() */ +static struct qe_firmware_info qe_firmware_info; + +/* + * Set to 1 if QE firmware has been uploaded, and therefore + * qe_firmware_info contains valid data. + */ +static int qe_firmware_uploaded; + +/* + * Upload a QE microcode + * + * This function is a worker function for qe_upload_firmware(). It does + * the actual uploading of the microcode. + */ +static void qe_upload_microcode(const void *base, + const struct qe_microcode *ucode) +{ + const __be32 *code = base + be32_to_cpu(ucode->code_offset); + unsigned int i; + + if (ucode->major || ucode->minor || ucode->revision) + printk(KERN_INFO "qe-firmware: " + "uploading microcode '%s' version %u.%u.%u\n", + ucode->id, ucode->major, ucode->minor, ucode->revision); + else + printk(KERN_INFO "qe-firmware: " + "uploading microcode '%s'\n", ucode->id); + + /* Use auto-increment */ + out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) | + QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR); + + for (i = 0; i < be32_to_cpu(ucode->count); i++) + out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i])); +} + +/* + * Upload a microcode to the I-RAM at a specific address. + * + * See Documentation/powerpc/qe-firmware.txt for information on QE microcode + * uploading. + * + * Currently, only version 1 is supported, so the 'version' field must be + * set to 1. + * + * The SOC model and revision are not validated, they are only displayed for + * informational purposes. + * + * 'calc_size' is the calculated size, in bytes, of the firmware structure and + * all of the microcode structures, minus the CRC. + * + * 'length' is the size that the structure says it is, including the CRC. + */ +int qe_upload_firmware(const struct qe_firmware *firmware) +{ + unsigned int i; + unsigned int j; + u32 crc; + size_t calc_size = sizeof(struct qe_firmware); + size_t length; + const struct qe_header *hdr; + + if (!firmware) { + printk(KERN_ERR "qe-firmware: invalid pointer\n"); + return -EINVAL; + } + + hdr = &firmware->header; + length = be32_to_cpu(hdr->length); + + /* Check the magic */ + if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || + (hdr->magic[2] != 'F')) { + printk(KERN_ERR "qe-firmware: not a microcode\n"); + return -EPERM; + } + + /* Check the version */ + if (hdr->version != 1) { + printk(KERN_ERR "qe-firmware: unsupported version\n"); + return -EPERM; + } + + /* Validate some of the fields */ + if ((firmware->count < 1) || (firmware->count >= MAX_QE_RISC)) { + printk(KERN_ERR "qe-firmware: invalid data\n"); + return -EINVAL; + } + + /* Validate the length and check if there's a CRC */ + calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); + + for (i = 0; i < firmware->count; i++) + /* + * For situations where the second RISC uses the same microcode + * as the first, the 'code_offset' and 'count' fields will be + * zero, so it's okay to add those. + */ + calc_size += sizeof(__be32) * + be32_to_cpu(firmware->microcode[i].count); + + /* Validate the length */ + if (length != calc_size + sizeof(__be32)) { + printk(KERN_ERR "qe-firmware: invalid length\n"); + return -EPERM; + } + + /* Validate the CRC */ + crc = be32_to_cpu(*(__be32 *)((void *)firmware + calc_size)); + if (crc != crc32(0, firmware, calc_size)) { + printk(KERN_ERR "qe-firmware: firmware CRC is invalid\n"); + return -EIO; + } + + /* + * If the microcode calls for it, split the I-RAM. + */ + if (!firmware->split) + setbits16(&qe_immr->cp.cercr, QE_CP_CERCR_CIR); + + if (firmware->soc.model) + printk(KERN_INFO + "qe-firmware: firmware '%s' for %u V%u.%u\n", + firmware->id, be16_to_cpu(firmware->soc.model), + firmware->soc.major, firmware->soc.minor); + else + printk(KERN_INFO "qe-firmware: firmware '%s'\n", + firmware->id); + + /* + * The QE only supports one microcode per RISC, so clear out all the + * saved microcode information and put in the new. + */ + memset(&qe_firmware_info, 0, sizeof(qe_firmware_info)); + strcpy(qe_firmware_info.id, firmware->id); + qe_firmware_info.extended_modes = firmware->extended_modes; + memcpy(qe_firmware_info.vtraps, firmware->vtraps, + sizeof(firmware->vtraps)); + + /* Loop through each microcode. */ + for (i = 0; i < firmware->count; i++) { + const struct qe_microcode *ucode = &firmware->microcode[i]; + + /* Upload a microcode if it's present */ + if (ucode->code_offset) + qe_upload_microcode(firmware, ucode); + + /* Program the traps for this processor */ + for (j = 0; j < 16; j++) { + u32 trap = be32_to_cpu(ucode->traps[j]); + + if (trap) + out_be32(&qe_immr->rsp[i].tibcr[j], trap); + } + + /* Enable traps */ + out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr)); + } + + qe_firmware_uploaded = 1; + + return 0; +} +EXPORT_SYMBOL(qe_upload_firmware); + +/* + * Get info on the currently-loaded firmware + * + * This function also checks the device tree to see if the boot loader has + * uploaded a firmware already. + */ +struct qe_firmware_info *qe_get_firmware_info(void) +{ + static int initialized; + struct property *prop; + struct device_node *qe; + struct device_node *fw = NULL; + const char *sprop; + unsigned int i; + + /* + * If we haven't checked yet, and a driver hasn't uploaded a firmware + * yet, then check the device tree for information. + */ + if (initialized || qe_firmware_uploaded) + return NULL; + + initialized = 1; + + /* + * Newer device trees have an "fsl,qe" compatible property for the QE + * node, but we still need to support older device trees. + */ + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!qe) { + qe = of_find_node_by_type(NULL, "qe"); + if (!qe) + return NULL; + } + + /* Find the 'firmware' child node */ + for_each_child_of_node(qe, fw) { + if (strcmp(fw->name, "firmware") == 0) + break; + } + + of_node_put(qe); + + /* Did we find the 'firmware' node? */ + if (!fw) + return NULL; + + qe_firmware_uploaded = 1; + + /* Copy the data into qe_firmware_info*/ + sprop = of_get_property(fw, "id", NULL); + if (sprop) + strncpy(qe_firmware_info.id, sprop, + sizeof(qe_firmware_info.id) - 1); + + prop = of_find_property(fw, "extended-modes", NULL); + if (prop && (prop->length == sizeof(u64))) { + const u64 *iprop = prop->value; + + qe_firmware_info.extended_modes = *iprop; + } + + prop = of_find_property(fw, "virtual-traps", NULL); + if (prop && (prop->length == 32)) { + const u32 *iprop = prop->value; + + for (i = 0; i < ARRAY_SIZE(qe_firmware_info.vtraps); i++) + qe_firmware_info.vtraps[i] = iprop[i]; + } + + of_node_put(fw); + + return &qe_firmware_info; +} +EXPORT_SYMBOL(qe_get_firmware_info); + diff --git a/arch/powerpc/sysdev/qe_lib/ucc_slow.c b/arch/powerpc/sysdev/qe_lib/ucc_slow.c index 0174b3aeef8..b2870b208dd 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc_slow.c +++ b/arch/powerpc/sysdev/qe_lib/ucc_slow.c @@ -19,6 +19,7 @@ #include <linux/stddef.h> #include <linux/interrupt.h> #include <linux/err.h> +#include <linux/module.h> #include <asm/io.h> #include <asm/immap_qe.h> @@ -41,6 +42,7 @@ u32 ucc_slow_get_qe_cr_subblock(int uccs_num) default: return QE_CR_SUBBLOCK_INVALID; } } +EXPORT_SYMBOL(ucc_slow_get_qe_cr_subblock); void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs) { @@ -56,6 +58,7 @@ void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs) qe_issue_cmd(QE_GRACEFUL_STOP_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); } +EXPORT_SYMBOL(ucc_slow_graceful_stop_tx); void ucc_slow_stop_tx(struct ucc_slow_private * uccs) { @@ -65,6 +68,7 @@ void ucc_slow_stop_tx(struct ucc_slow_private * uccs) id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); qe_issue_cmd(QE_STOP_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); } +EXPORT_SYMBOL(ucc_slow_stop_tx); void ucc_slow_restart_tx(struct ucc_slow_private * uccs) { @@ -74,6 +78,7 @@ void ucc_slow_restart_tx(struct ucc_slow_private * uccs) id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); qe_issue_cmd(QE_RESTART_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); } +EXPORT_SYMBOL(ucc_slow_restart_tx); void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode) { @@ -94,6 +99,7 @@ void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode) } out_be32(&us_regs->gumr_l, gumr_l); } +EXPORT_SYMBOL(ucc_slow_enable); void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode) { @@ -114,6 +120,7 @@ void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode) } out_be32(&us_regs->gumr_l, gumr_l); } +EXPORT_SYMBOL(ucc_slow_disable); /* Initialize the UCC for Slow operations * @@ -347,6 +354,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc *uccs_ret = uccs; return 0; } +EXPORT_SYMBOL(ucc_slow_init); void ucc_slow_free(struct ucc_slow_private * uccs) { @@ -366,5 +374,5 @@ void ucc_slow_free(struct ucc_slow_private * uccs) kfree(uccs); } - +EXPORT_SYMBOL(ucc_slow_free); diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c index a113d800cbf..be2808a292f 100644 --- a/arch/powerpc/sysdev/tsi108_dev.c +++ b/arch/powerpc/sysdev/tsi108_dev.c @@ -66,14 +66,12 @@ EXPORT_SYMBOL(get_vir_csrbase); static int __init tsi108_eth_of_init(void) { struct device_node *np; - unsigned int i; + unsigned int i = 0; struct platform_device *tsi_eth_dev; struct resource res; int ret; - for (np = NULL, i = 0; - (np = of_find_compatible_node(np, "network", "tsi108-ethernet")) != NULL; - i++) { + for_each_compatible_node(np, "network", "tsi108-ethernet") { struct resource r[2]; struct device_node *phy, *mdio; hw_info tsi_eth_data; @@ -98,7 +96,7 @@ static int __init tsi108_eth_of_init(void) __FUNCTION__,r[1].name, r[1].start, r[1].end); tsi_eth_dev = - platform_device_register_simple("tsi-ethernet", i, &r[0], + platform_device_register_simple("tsi-ethernet", i++, &r[0], 1); if (IS_ERR(tsi_eth_dev)) { @@ -154,6 +152,7 @@ static int __init tsi108_eth_of_init(void) unreg: platform_device_unregister(tsi_eth_dev); err: + of_node_put(np); return ret; } diff --git a/arch/powerpc/sysdev/uic.c b/arch/powerpc/sysdev/uic.c index 847a5496b86..625b275c379 100644 --- a/arch/powerpc/sysdev/uic.c +++ b/arch/powerpc/sysdev/uic.c @@ -53,21 +53,23 @@ struct uic { /* The remapper for this UIC */ struct irq_host *irqhost; - - /* For secondary UICs, the cascade interrupt's irqaction */ - struct irqaction cascade; }; static void uic_unmask_irq(unsigned int virq) { + struct irq_desc *desc = get_irq_desc(virq); struct uic *uic = get_irq_chip_data(virq); unsigned int src = uic_irq_to_hw(virq); unsigned long flags; - u32 er; + u32 er, sr; + sr = 1 << (31-src); spin_lock_irqsave(&uic->lock, flags); + /* ack level-triggered interrupts here */ + if (desc->status & IRQ_LEVEL) + mtdcr(uic->dcrbase + UIC_SR, sr); er = mfdcr(uic->dcrbase + UIC_ER); - er |= 1 << (31 - src); + er |= sr; mtdcr(uic->dcrbase + UIC_ER, er); spin_unlock_irqrestore(&uic->lock, flags); } @@ -99,6 +101,7 @@ static void uic_ack_irq(unsigned int virq) static void uic_mask_ack_irq(unsigned int virq) { + struct irq_desc *desc = get_irq_desc(virq); struct uic *uic = get_irq_chip_data(virq); unsigned int src = uic_irq_to_hw(virq); unsigned long flags; @@ -109,7 +112,16 @@ static void uic_mask_ack_irq(unsigned int virq) er = mfdcr(uic->dcrbase + UIC_ER); er &= ~sr; mtdcr(uic->dcrbase + UIC_ER, er); - mtdcr(uic->dcrbase + UIC_SR, sr); + /* On the UIC, acking (i.e. clearing the SR bit) + * a level irq will have no effect if the interrupt + * is still asserted by the device, even if + * the interrupt is already masked. Therefore + * we only ack the egde interrupts here, while + * level interrupts are ack'ed after the actual + * isr call in the uic_unmask_irq() + */ + if (!(desc->status & IRQ_LEVEL)) + mtdcr(uic->dcrbase + UIC_SR, sr); spin_unlock_irqrestore(&uic->lock, flags); } @@ -173,64 +185,6 @@ static struct irq_chip uic_irq_chip = { .set_type = uic_set_irq_type, }; -/** - * handle_uic_irq - irq flow handler for UIC - * @irq: the interrupt number - * @desc: the interrupt description structure for this irq - * - * This is modified version of the generic handle_level_irq() suitable - * for the UIC. On the UIC, acking (i.e. clearing the SR bit) a level - * irq will have no effect if the interrupt is still asserted by the - * device, even if the interrupt is already masked. Therefore, unlike - * the standard handle_level_irq(), we must ack the interrupt *after* - * invoking the ISR (which should have de-asserted the interrupt in - * the external source). For edge interrupts we ack at the beginning - * instead of the end, to keep the window in which we can miss an - * interrupt as small as possible. - */ -void fastcall handle_uic_irq(unsigned int irq, struct irq_desc *desc) -{ - unsigned int cpu = smp_processor_id(); - struct irqaction *action; - irqreturn_t action_ret; - - spin_lock(&desc->lock); - if (desc->status & IRQ_LEVEL) - desc->chip->mask(irq); - else - desc->chip->mask_ack(irq); - - if (unlikely(desc->status & IRQ_INPROGRESS)) - goto out_unlock; - desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); - kstat_cpu(cpu).irqs[irq]++; - - /* - * If its disabled or no action available - * keep it masked and get out of here - */ - action = desc->action; - if (unlikely(!action || (desc->status & IRQ_DISABLED))) { - desc->status |= IRQ_PENDING; - goto out_unlock; - } - - desc->status |= IRQ_INPROGRESS; - desc->status &= ~IRQ_PENDING; - spin_unlock(&desc->lock); - - action_ret = handle_IRQ_event(irq, action); - - spin_lock(&desc->lock); - desc->status &= ~IRQ_INPROGRESS; - if (desc->status & IRQ_LEVEL) - desc->chip->ack(irq); - if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) - desc->chip->unmask(irq); -out_unlock: - spin_unlock(&desc->lock); -} - static int uic_host_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hw) { @@ -239,7 +193,7 @@ static int uic_host_map(struct irq_host *h, unsigned int virq, set_irq_chip_data(virq, uic); /* Despite the name, handle_level_irq() works for both level * and edge irqs on UIC. FIXME: check this is correct */ - set_irq_chip_and_handler(virq, &uic_irq_chip, handle_uic_irq); + set_irq_chip_and_handler(virq, &uic_irq_chip, handle_level_irq); /* Set default irq type */ set_irq_type(virq, IRQ_TYPE_NONE); @@ -264,23 +218,36 @@ static struct irq_host_ops uic_host_ops = { .xlate = uic_host_xlate, }; -irqreturn_t uic_cascade(int virq, void *data) +void uic_irq_cascade(unsigned int virq, struct irq_desc *desc) { - struct uic *uic = data; + struct uic *uic = get_irq_data(virq); u32 msr; int src; int subvirq; + spin_lock(&desc->lock); + if (desc->status & IRQ_LEVEL) + desc->chip->mask(virq); + else + desc->chip->mask_ack(virq); + spin_unlock(&desc->lock); + msr = mfdcr(uic->dcrbase + UIC_MSR); if (!msr) /* spurious interrupt */ - return IRQ_HANDLED; + goto uic_irq_ret; src = 32 - ffs(msr); subvirq = irq_linear_revmap(uic->irqhost, src); generic_handle_irq(subvirq); - return IRQ_HANDLED; +uic_irq_ret: + spin_lock(&desc->lock); + if (desc->status & IRQ_LEVEL) + desc->chip->ack(virq); + if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) + desc->chip->unmask(virq); + spin_unlock(&desc->lock); } static struct uic * __init uic_init_one(struct device_node *node) @@ -342,33 +309,27 @@ void __init uic_init_tree(void) const u32 *interrupts; /* First locate and initialize the top-level UIC */ - - np = of_find_compatible_node(NULL, NULL, "ibm,uic"); - while (np) { + for_each_compatible_node(np, NULL, "ibm,uic") { interrupts = of_get_property(np, "interrupts", NULL); - if (! interrupts) + if (!interrupts) break; - - np = of_find_compatible_node(np, NULL, "ibm,uic"); } BUG_ON(!np); /* uic_init_tree() assumes there's a UIC as the * top-level interrupt controller */ primary_uic = uic_init_one(np); - if (! primary_uic) + if (!primary_uic) panic("Unable to initialize primary UIC %s\n", np->full_name); irq_set_default_host(primary_uic->irqhost); of_node_put(np); /* The scan again for cascaded UICs */ - np = of_find_compatible_node(NULL, NULL, "ibm,uic"); - while (np) { + for_each_compatible_node(np, NULL, "ibm,uic") { interrupts = of_get_property(np, "interrupts", NULL); if (interrupts) { /* Secondary UIC */ int cascade_virq; - int ret; uic = uic_init_one(np); if (! uic) @@ -377,20 +338,11 @@ void __init uic_init_tree(void) cascade_virq = irq_of_parse_and_map(np, 0); - uic->cascade.handler = uic_cascade; - uic->cascade.name = "UIC cascade"; - uic->cascade.dev_id = uic; - - ret = setup_irq(cascade_virq, &uic->cascade); - if (ret) - printk(KERN_ERR "Failed to setup_irq(%d) for " - "UIC%d cascade\n", cascade_virq, - uic->index); + set_irq_data(cascade_virq, uic); + set_irq_chained_handler(cascade_virq, uic_irq_cascade); /* FIXME: setup critical cascade?? */ } - - np = of_find_compatible_node(np, NULL, "ibm,uic"); } } diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c index c2f17cc43df..ba8eea2bcce 100644 --- a/arch/powerpc/sysdev/xilinx_intc.c +++ b/arch/powerpc/sysdev/xilinx_intc.c @@ -135,10 +135,16 @@ void __init xilinx_intc_init_tree(void) struct device_node *np; /* find top level interrupt controller */ - for_each_compatible_node(np, NULL, "xilinx,intc") { + for_each_compatible_node(np, NULL, "xlnx,opb-intc-1.00.c") { if (!of_get_property(np, "interrupts", NULL)) break; } + if (!np) { + for_each_compatible_node(np, NULL, "xlnx,xps-intc-1.00.a") { + if (!of_get_property(np, "interrupts", NULL)) + break; + } + } /* xilinx interrupt controller needs to be top level */ BUG_ON(!np); |