linux/arch/powerpc/sysdev/mv64x60_pci.c
<<
>>
Prefs
   1/*
   2 * PCI bus setup for Marvell mv64360/mv64460 host bridges (Discovery)
   3 *
   4 * Author: Dale Farnsworth <dale@farnsworth.org>
   5 *
   6 * 2007 (c) MontaVista, Software, Inc.  This file is licensed under
   7 * the terms of the GNU General Public License version 2.  This program
   8 * is licensed "as is" without any warranty of any kind, whether express
   9 * or implied.
  10 */
  11
  12#include <linux/stddef.h>
  13#include <linux/kernel.h>
  14#include <linux/init.h>
  15#include <linux/stat.h>
  16#include <linux/pci.h>
  17
  18#include <asm/prom.h>
  19#include <asm/pci-bridge.h>
  20
  21#define PCI_HEADER_TYPE_INVALID         0x7f    /* Invalid PCI header type */
  22
  23#ifdef CONFIG_SYSFS
  24/* 32-bit hex or dec stringified number + '\n' */
  25#define MV64X60_VAL_LEN_MAX             11
  26#define MV64X60_PCICFG_CPCI_HOTSWAP     0x68
  27
  28static ssize_t mv64x60_hs_reg_read(struct file *filp, struct kobject *kobj,
  29                                   struct bin_attribute *attr, char *buf,
  30                                   loff_t off, size_t count)
  31{
  32        struct pci_dev *phb;
  33        u32 v;
  34
  35        if (off > 0)
  36                return 0;
  37        if (count < MV64X60_VAL_LEN_MAX)
  38                return -EINVAL;
  39
  40        phb = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
  41        if (!phb)
  42                return -ENODEV;
  43        pci_read_config_dword(phb, MV64X60_PCICFG_CPCI_HOTSWAP, &v);
  44        pci_dev_put(phb);
  45
  46        return sprintf(buf, "0x%08x\n", v);
  47}
  48
  49static ssize_t mv64x60_hs_reg_write(struct file *filp, struct kobject *kobj,
  50                                    struct bin_attribute *attr, char *buf,
  51                                    loff_t off, size_t count)
  52{
  53        struct pci_dev *phb;
  54        u32 v;
  55
  56        if (off > 0)
  57                return 0;
  58        if (count <= 0)
  59                return -EINVAL;
  60
  61        if (sscanf(buf, "%i", &v) != 1)
  62                return -EINVAL;
  63
  64        phb = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
  65        if (!phb)
  66                return -ENODEV;
  67        pci_write_config_dword(phb, MV64X60_PCICFG_CPCI_HOTSWAP, v);
  68        pci_dev_put(phb);
  69
  70        return count;
  71}
  72
  73static struct bin_attribute mv64x60_hs_reg_attr = { /* Hotswap register */
  74        .attr = {
  75                .name = "hs_reg",
  76                .mode = S_IRUGO | S_IWUSR,
  77        },
  78        .size  = MV64X60_VAL_LEN_MAX,
  79        .read  = mv64x60_hs_reg_read,
  80        .write = mv64x60_hs_reg_write,
  81};
  82
  83static int __init mv64x60_sysfs_init(void)
  84{
  85        struct device_node *np;
  86        struct platform_device *pdev;
  87        const unsigned int *prop;
  88
  89        np = of_find_compatible_node(NULL, NULL, "marvell,mv64360");
  90        if (!np)
  91                return 0;
  92
  93        prop = of_get_property(np, "hs_reg_valid", NULL);
  94        of_node_put(np);
  95
  96        pdev = platform_device_register_simple("marvell,mv64360", 0, NULL, 0);
  97        if (IS_ERR(pdev))
  98                return PTR_ERR(pdev);
  99
 100        return sysfs_create_bin_file(&pdev->dev.kobj, &mv64x60_hs_reg_attr);
 101}
 102
 103subsys_initcall(mv64x60_sysfs_init);
 104
 105#endif /* CONFIG_SYSFS */
 106
 107static void mv64x60_pci_fixup_early(struct pci_dev *dev)
 108{
 109        /*
 110         * Set the host bridge hdr_type to an invalid value so that
 111         * pci_setup_device() will ignore the host bridge.
 112         */
 113        dev->hdr_type = PCI_HEADER_TYPE_INVALID;
 114}
 115DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64360,
 116                        mv64x60_pci_fixup_early);
 117DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64460,
 118                        mv64x60_pci_fixup_early);
 119
 120static int __init mv64x60_add_bridge(struct device_node *dev)
 121{
 122        int len;
 123        struct pci_controller *hose;
 124        struct resource rsrc;
 125        const int *bus_range;
 126        int primary;
 127
 128        memset(&rsrc, 0, sizeof(rsrc));
 129
 130        /* Fetch host bridge registers address */
 131        if (of_address_to_resource(dev, 0, &rsrc)) {
 132                printk(KERN_ERR "No PCI reg property in device tree\n");
 133                return -ENODEV;
 134        }
 135
 136        /* Get bus range if any */
 137        bus_range = of_get_property(dev, "bus-range", &len);
 138        if (bus_range == NULL || len < 2 * sizeof(int))
 139                printk(KERN_WARNING "Can't get bus-range for %s, assume"
 140                       " bus 0\n", dev->full_name);
 141
 142        hose = pcibios_alloc_controller(dev);
 143        if (!hose)
 144                return -ENOMEM;
 145
 146        hose->first_busno = bus_range ? bus_range[0] : 0;
 147        hose->last_busno = bus_range ? bus_range[1] : 0xff;
 148
 149        setup_indirect_pci(hose, rsrc.start, rsrc.start + 4, 0);
 150        hose->self_busno = hose->first_busno;
 151
 152        printk(KERN_INFO "Found MV64x60 PCI host bridge at 0x%016llx. "
 153               "Firmware bus number: %d->%d\n",
 154               (unsigned long long)rsrc.start, hose->first_busno,
 155               hose->last_busno);
 156
 157        /* Interpret the "ranges" property */
 158        /* This also maps the I/O region and sets isa_io/mem_base */
 159        primary = (hose->first_busno == 0);
 160        pci_process_bridge_OF_ranges(hose, dev, primary);
 161
 162        return 0;
 163}
 164
 165void __init mv64x60_pci_init(void)
 166{
 167        struct device_node *np;
 168
 169        for_each_compatible_node(np, "pci", "marvell,mv64360-pci")
 170                mv64x60_add_bridge(np);
 171}
 172