| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 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> | 
| Paul Gortmaker | b56eade | 2011-05-27 13:27:45 -0400 | [diff] [blame] | 15 | #include <linux/stat.h> | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 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 |  | 
| Chris Wright | 2c3c8be | 2010-05-12 18:28:57 -0700 | [diff] [blame] | 28 | static ssize_t mv64x60_hs_reg_read(struct file *filp, struct kobject *kobj, | 
| Dale Farnsworth | d102c9d | 2007-10-24 05:20:32 +1000 | [diff] [blame] | 29 | struct bin_attribute *attr, char *buf, | 
|  | 30 | loff_t off, size_t count) | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 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 |  | 
| Chris Wright | 2c3c8be | 2010-05-12 18:28:57 -0700 | [diff] [blame] | 49 | static ssize_t mv64x60_hs_reg_write(struct file *filp, struct kobject *kobj, | 
| Dale Farnsworth | d102c9d | 2007-10-24 05:20:32 +1000 | [diff] [blame] | 50 | struct bin_attribute *attr, char *buf, | 
|  | 51 | loff_t off, size_t count) | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 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 |  | 
|  | 73 | static struct bin_attribute mv64x60_hs_reg_attr = { /* Hotswap register */ | 
|  | 74 | .attr = { | 
|  | 75 | .name = "hs_reg", | 
|  | 76 | .mode = S_IRUGO | S_IWUSR, | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 77 | }, | 
|  | 78 | .size  = MV64X60_VAL_LEN_MAX, | 
|  | 79 | .read  = mv64x60_hs_reg_read, | 
|  | 80 | .write = mv64x60_hs_reg_write, | 
|  | 81 | }; | 
|  | 82 |  | 
|  | 83 | static int __init mv64x60_sysfs_init(void) | 
|  | 84 | { | 
|  | 85 | struct device_node *np; | 
|  | 86 | struct platform_device *pdev; | 
|  | 87 | const unsigned int *prop; | 
|  | 88 |  | 
| Mark A. Greer | a1810b4 | 2008-04-08 08:09:03 +1000 | [diff] [blame] | 89 | np = of_find_compatible_node(NULL, NULL, "marvell,mv64360"); | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 90 | if (!np) | 
|  | 91 | return 0; | 
|  | 92 |  | 
|  | 93 | prop = of_get_property(np, "hs_reg_valid", NULL); | 
|  | 94 | of_node_put(np); | 
|  | 95 |  | 
| Mark A. Greer | a1810b4 | 2008-04-08 08:09:03 +1000 | [diff] [blame] | 96 | pdev = platform_device_register_simple("marvell,mv64360", 0, NULL, 0); | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 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 |  | 
|  | 103 | subsys_initcall(mv64x60_sysfs_init); | 
|  | 104 |  | 
|  | 105 | #endif /* CONFIG_SYSFS */ | 
|  | 106 |  | 
| Sebastian Andrzej Siewior | 492bf4f | 2012-06-03 20:48:24 +0200 | [diff] [blame] | 107 | static void __devinit mv64x60_pci_fixup_early(struct pci_dev *dev) | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 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 | } | 
|  | 115 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64360, | 
|  | 116 | mv64x60_pci_fixup_early); | 
|  | 117 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64460, | 
|  | 118 | mv64x60_pci_fixup_early); | 
|  | 119 |  | 
|  | 120 | static 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 |  | 
| Kumar Gala | dbf8471 | 2007-06-27 01:56:50 -0500 | [diff] [blame] | 142 | hose = pcibios_alloc_controller(dev); | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 143 | if (!hose) | 
|  | 144 | return -ENOMEM; | 
|  | 145 |  | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 146 | hose->first_busno = bus_range ? bus_range[0] : 0; | 
|  | 147 | hose->last_busno = bus_range ? bus_range[1] : 0xff; | 
|  | 148 |  | 
| Kumar Gala | 2e56ff2 | 2007-07-19 16:07:35 -0500 | [diff] [blame] | 149 | setup_indirect_pci(hose, rsrc.start, rsrc.start + 4, 0); | 
| Kumar Gala | 0a3786c | 2007-06-25 13:32:48 -0500 | [diff] [blame] | 150 | hose->self_busno = hose->first_busno; | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 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 |  | 
|  | 165 | void __init mv64x60_pci_init(void) | 
|  | 166 | { | 
| Cyrill Gorcunov | 26cb7d8 | 2007-11-30 06:44:36 +1100 | [diff] [blame] | 167 | struct device_node *np; | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 168 |  | 
| Mark A. Greer | a1810b4 | 2008-04-08 08:09:03 +1000 | [diff] [blame] | 169 | for_each_compatible_node(np, "pci", "marvell,mv64360-pci") | 
| Dale Farnsworth | e1a3107 | 2007-05-12 10:57:12 +1000 | [diff] [blame] | 170 | mv64x60_add_bridge(np); | 
|  | 171 | } |