From e1a3107b06a9619773596cd46a9ce0574419aed4 Mon Sep 17 00:00:00 2001 From: Dale Farnsworth Date: Sat, 12 May 2007 10:57:12 +1000 Subject: [PATCH] [POWERPC] Add Marvell mv64x60 PCI bridge support This patch adds PCI bridge support for the Marvell mv64x60 chip. We also provide the ability to read/write the mv64x60 hotswap register via sysfs if the hs_reg_valid property is set in the device tree. Signed-off-by: Dale Farnsworth Acked-by: Arnd Bergmann Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/embedded6xx/Kconfig | 1 + arch/powerpc/sysdev/Makefile | 3 +- arch/powerpc/sysdev/mv64x60.h | 2 + arch/powerpc/sysdev/mv64x60_pci.c | 172 +++++++++++++++++++++ 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/sysdev/mv64x60_pci.c diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig index abb27c97afe9..bafd05765a72 100644 --- a/arch/powerpc/platforms/embedded6xx/Kconfig +++ b/arch/powerpc/platforms/embedded6xx/Kconfig @@ -48,6 +48,7 @@ config MPC10X_BRIDGE config MV64X60 bool + select PPC_INDIRECT_PCI config MPC10X_OPENPIC bool diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 452ff5636e41..c3ce0bd12c0b 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -16,7 +16,8 @@ obj-$(CONFIG_FSL_SOC) += fsl_soc.o obj-$(CONFIG_FSL_PCIE) += fsl_pcie.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ -obj-$(CONFIG_MV64X60) += mv64x60_pic.o mv64x60_dev.o +mv64x60-$(CONFIG_PCI) += mv64x60_pci.o +obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o # contains only the suspend handler for time obj-$(CONFIG_PM) += timer.o diff --git a/arch/powerpc/sysdev/mv64x60.h b/arch/powerpc/sysdev/mv64x60.h index 709003948ae7..2ff0b4ef2681 100644 --- a/arch/powerpc/sysdev/mv64x60.h +++ b/arch/powerpc/sysdev/mv64x60.h @@ -6,4 +6,6 @@ extern void __init mv64x60_init_irq(void); extern unsigned int mv64x60_get_irq(void); +extern void __init mv64x60_pci_init(void); + #endif /* __MV64X60_H__ */ diff --git a/arch/powerpc/sysdev/mv64x60_pci.c b/arch/powerpc/sysdev/mv64x60_pci.c new file mode 100644 index 000000000000..b5aef4cbc8d2 --- /dev/null +++ b/arch/powerpc/sysdev/mv64x60_pci.c @@ -0,0 +1,172 @@ +/* + * PCI bus setup for Marvell mv64360/mv64460 host bridges (Discovery) + * + * Author: Dale Farnsworth + * + * 2007 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include + +#include +#include + +#define PCI_HEADER_TYPE_INVALID 0x7f /* Invalid PCI header type */ + +#ifdef CONFIG_SYSFS +/* 32-bit hex or dec stringified number + '\n' */ +#define MV64X60_VAL_LEN_MAX 11 +#define MV64X60_PCICFG_CPCI_HOTSWAP 0x68 + +static ssize_t mv64x60_hs_reg_read(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct pci_dev *phb; + u32 v; + + if (off > 0) + return 0; + if (count < MV64X60_VAL_LEN_MAX) + return -EINVAL; + + phb = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)); + if (!phb) + return -ENODEV; + pci_read_config_dword(phb, MV64X60_PCICFG_CPCI_HOTSWAP, &v); + pci_dev_put(phb); + + return sprintf(buf, "0x%08x\n", v); +} + +static ssize_t mv64x60_hs_reg_write(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct pci_dev *phb; + u32 v; + + if (off > 0) + return 0; + if (count <= 0) + return -EINVAL; + + if (sscanf(buf, "%i", &v) != 1) + return -EINVAL; + + phb = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)); + if (!phb) + return -ENODEV; + pci_write_config_dword(phb, MV64X60_PCICFG_CPCI_HOTSWAP, v); + pci_dev_put(phb); + + return count; +} + +static struct bin_attribute mv64x60_hs_reg_attr = { /* Hotswap register */ + .attr = { + .name = "hs_reg", + .mode = S_IRUGO | S_IWUSR, + .owner = THIS_MODULE, + }, + .size = MV64X60_VAL_LEN_MAX, + .read = mv64x60_hs_reg_read, + .write = mv64x60_hs_reg_write, +}; + +static int __init mv64x60_sysfs_init(void) +{ + struct device_node *np; + struct platform_device *pdev; + const unsigned int *prop; + + np = of_find_compatible_node(NULL, NULL, "marvell,mv64x60"); + if (!np) + return 0; + + prop = of_get_property(np, "hs_reg_valid", NULL); + of_node_put(np); + + pdev = platform_device_register_simple("marvell,mv64x60", 0, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + return sysfs_create_bin_file(&pdev->dev.kobj, &mv64x60_hs_reg_attr); +} + +subsys_initcall(mv64x60_sysfs_init); + +#endif /* CONFIG_SYSFS */ + +static void __init mv64x60_pci_fixup_early(struct pci_dev *dev) +{ + /* + * Set the host bridge hdr_type to an invalid value so that + * pci_setup_device() will ignore the host bridge. + */ + dev->hdr_type = PCI_HEADER_TYPE_INVALID; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64360, + mv64x60_pci_fixup_early); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64460, + mv64x60_pci_fixup_early); + +static int __init mv64x60_add_bridge(struct device_node *dev) +{ + int len; + struct pci_controller *hose; + struct resource rsrc; + const int *bus_range; + int primary; + + memset(&rsrc, 0, sizeof(rsrc)); + + /* Fetch host bridge registers address */ + if (of_address_to_resource(dev, 0, &rsrc)) { + printk(KERN_ERR "No PCI reg property in device tree\n"); + return -ENODEV; + } + + /* Get bus range if any */ + bus_range = of_get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) + printk(KERN_WARNING "Can't get bus-range for %s, assume" + " bus 0\n", dev->full_name); + + hose = pcibios_alloc_controller(); + if (!hose) + return -ENOMEM; + + hose->arch_data = dev; + hose->set_cfg_type = 1; + + hose->first_busno = bus_range ? bus_range[0] : 0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + setup_indirect_pci(hose, rsrc.start, rsrc.start + 4); + hose->bus_offset = hose->first_busno; + + printk(KERN_INFO "Found MV64x60 PCI host bridge at 0x%016llx. " + "Firmware bus number: %d->%d\n", + (unsigned long long)rsrc.start, hose->first_busno, + hose->last_busno); + + /* Interpret the "ranges" property */ + /* This also maps the I/O region and sets isa_io/mem_base */ + primary = (hose->first_busno == 0); + pci_process_bridge_OF_ranges(hose, dev, primary); + + return 0; +} + +void __init mv64x60_pci_init(void) +{ + struct device_node *np = NULL; + + while ((np = of_find_compatible_node(np, "pci", "marvell,mv64x60-pci"))) + mv64x60_add_bridge(np); +}