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