| Stanislav Samsonov | 794d15b | 2008-06-22 22:45:10 +0200 | [diff] [blame] | 1 | /* | 
 | 2 |  * arch/arm/mach-mv78xx0/pcie.c | 
 | 3 |  * | 
 | 4 |  * PCIe functions for Marvell MV78xx0 SoCs | 
 | 5 |  * | 
 | 6 |  * This file is licensed under the terms of the GNU General Public | 
 | 7 |  * License version 2.  This program is licensed "as is" without any | 
 | 8 |  * warranty of any kind, whether express or implied. | 
 | 9 |  */ | 
 | 10 |  | 
 | 11 | #include <linux/kernel.h> | 
 | 12 | #include <linux/pci.h> | 
 | 13 | #include <linux/mbus.h> | 
| Nicolas Pitre | ba0cda6 | 2009-01-07 04:58:23 +0100 | [diff] [blame] | 14 | #include <asm/irq.h> | 
| Stanislav Samsonov | 794d15b | 2008-06-22 22:45:10 +0200 | [diff] [blame] | 15 | #include <asm/mach/pci.h> | 
| Lennert Buytenhek | 6f088f1 | 2008-08-09 13:44:58 +0200 | [diff] [blame] | 16 | #include <plat/pcie.h> | 
| Stanislav Samsonov | 794d15b | 2008-06-22 22:45:10 +0200 | [diff] [blame] | 17 | #include "common.h" | 
 | 18 |  | 
 | 19 | struct pcie_port { | 
 | 20 | 	u8			maj; | 
 | 21 | 	u8			min; | 
 | 22 | 	u8			root_bus_nr; | 
 | 23 | 	void __iomem		*base; | 
 | 24 | 	spinlock_t		conf_lock; | 
 | 25 | 	char			io_space_name[16]; | 
 | 26 | 	char			mem_space_name[16]; | 
 | 27 | 	struct resource		res[2]; | 
 | 28 | }; | 
 | 29 |  | 
 | 30 | static struct pcie_port pcie_port[8]; | 
 | 31 | static int num_pcie_ports; | 
 | 32 | static struct resource pcie_io_space; | 
 | 33 | static struct resource pcie_mem_space; | 
 | 34 |  | 
 | 35 |  | 
| Lennert Buytenhek | cfdeb63 | 2009-02-20 02:31:35 +0100 | [diff] [blame] | 36 | void __init mv78xx0_pcie_id(u32 *dev, u32 *rev) | 
 | 37 | { | 
 | 38 | 	*dev = orion_pcie_dev_id((void __iomem *)PCIE00_VIRT_BASE); | 
 | 39 | 	*rev = orion_pcie_rev((void __iomem *)PCIE00_VIRT_BASE); | 
 | 40 | } | 
 | 41 |  | 
| Stanislav Samsonov | 794d15b | 2008-06-22 22:45:10 +0200 | [diff] [blame] | 42 | static void __init mv78xx0_pcie_preinit(void) | 
 | 43 | { | 
 | 44 | 	int i; | 
 | 45 | 	u32 size_each; | 
 | 46 | 	u32 start; | 
 | 47 | 	int win; | 
 | 48 |  | 
 | 49 | 	pcie_io_space.name = "PCIe I/O Space"; | 
 | 50 | 	pcie_io_space.start = MV78XX0_PCIE_IO_PHYS_BASE(0); | 
 | 51 | 	pcie_io_space.end = | 
 | 52 | 		MV78XX0_PCIE_IO_PHYS_BASE(0) + MV78XX0_PCIE_IO_SIZE * 8 - 1; | 
 | 53 | 	pcie_io_space.flags = IORESOURCE_IO; | 
 | 54 | 	if (request_resource(&iomem_resource, &pcie_io_space)) | 
 | 55 | 		panic("can't allocate PCIe I/O space"); | 
 | 56 |  | 
 | 57 | 	pcie_mem_space.name = "PCIe MEM Space"; | 
 | 58 | 	pcie_mem_space.start = MV78XX0_PCIE_MEM_PHYS_BASE; | 
 | 59 | 	pcie_mem_space.end = | 
 | 60 | 		MV78XX0_PCIE_MEM_PHYS_BASE + MV78XX0_PCIE_MEM_SIZE - 1; | 
 | 61 | 	pcie_mem_space.flags = IORESOURCE_MEM; | 
 | 62 | 	if (request_resource(&iomem_resource, &pcie_mem_space)) | 
 | 63 | 		panic("can't allocate PCIe MEM space"); | 
 | 64 |  | 
 | 65 | 	for (i = 0; i < num_pcie_ports; i++) { | 
 | 66 | 		struct pcie_port *pp = pcie_port + i; | 
 | 67 |  | 
 | 68 | 		snprintf(pp->io_space_name, sizeof(pp->io_space_name), | 
 | 69 | 			"PCIe %d.%d I/O", pp->maj, pp->min); | 
 | 70 | 		pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0; | 
 | 71 | 		pp->res[0].name = pp->io_space_name; | 
 | 72 | 		pp->res[0].start = MV78XX0_PCIE_IO_PHYS_BASE(i); | 
 | 73 | 		pp->res[0].end = pp->res[0].start + MV78XX0_PCIE_IO_SIZE - 1; | 
 | 74 | 		pp->res[0].flags = IORESOURCE_IO; | 
 | 75 |  | 
 | 76 | 		snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), | 
 | 77 | 			"PCIe %d.%d MEM", pp->maj, pp->min); | 
 | 78 | 		pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; | 
 | 79 | 		pp->res[1].name = pp->mem_space_name; | 
 | 80 | 		pp->res[1].flags = IORESOURCE_MEM; | 
 | 81 | 	} | 
 | 82 |  | 
 | 83 | 	switch (num_pcie_ports) { | 
 | 84 | 	case 0: | 
 | 85 | 		size_each = 0; | 
 | 86 | 		break; | 
 | 87 |  | 
 | 88 | 	case 1: | 
 | 89 | 		size_each = 0x30000000; | 
 | 90 | 		break; | 
 | 91 |  | 
 | 92 | 	case 2 ... 3: | 
 | 93 | 		size_each = 0x10000000; | 
 | 94 | 		break; | 
 | 95 |  | 
 | 96 | 	case 4 ... 6: | 
 | 97 | 		size_each = 0x08000000; | 
 | 98 | 		break; | 
 | 99 |  | 
 | 100 | 	case 7: | 
 | 101 | 		size_each = 0x04000000; | 
 | 102 | 		break; | 
 | 103 |  | 
 | 104 | 	default: | 
 | 105 | 		panic("invalid number of PCIe ports"); | 
 | 106 | 	} | 
 | 107 |  | 
 | 108 | 	start = MV78XX0_PCIE_MEM_PHYS_BASE; | 
 | 109 | 	for (i = 0; i < num_pcie_ports; i++) { | 
 | 110 | 		struct pcie_port *pp = pcie_port + i; | 
 | 111 |  | 
 | 112 | 		pp->res[1].start = start; | 
 | 113 | 		pp->res[1].end = start + size_each - 1; | 
 | 114 | 		start += size_each; | 
 | 115 | 	} | 
 | 116 |  | 
 | 117 | 	for (i = 0; i < num_pcie_ports; i++) { | 
 | 118 | 		struct pcie_port *pp = pcie_port + i; | 
 | 119 |  | 
 | 120 | 		if (request_resource(&pcie_io_space, &pp->res[0])) | 
 | 121 | 			panic("can't allocate PCIe I/O sub-space"); | 
 | 122 |  | 
 | 123 | 		if (request_resource(&pcie_mem_space, &pp->res[1])) | 
 | 124 | 			panic("can't allocate PCIe MEM sub-space"); | 
 | 125 | 	} | 
 | 126 |  | 
 | 127 | 	win = 0; | 
 | 128 | 	for (i = 0; i < num_pcie_ports; i++) { | 
 | 129 | 		struct pcie_port *pp = pcie_port + i; | 
 | 130 |  | 
 | 131 | 		mv78xx0_setup_pcie_io_win(win++, pp->res[0].start, | 
 | 132 | 			pp->res[0].end - pp->res[0].start + 1, | 
 | 133 | 			pp->maj, pp->min); | 
 | 134 |  | 
 | 135 | 		mv78xx0_setup_pcie_mem_win(win++, pp->res[1].start, | 
 | 136 | 			pp->res[1].end - pp->res[1].start + 1, | 
 | 137 | 			pp->maj, pp->min); | 
 | 138 | 	} | 
 | 139 | } | 
 | 140 |  | 
 | 141 | static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys) | 
 | 142 | { | 
 | 143 | 	struct pcie_port *pp; | 
 | 144 |  | 
 | 145 | 	if (nr >= num_pcie_ports) | 
 | 146 | 		return 0; | 
 | 147 |  | 
 | 148 | 	pp = &pcie_port[nr]; | 
 | 149 | 	pp->root_bus_nr = sys->busnr; | 
 | 150 |  | 
 | 151 | 	/* | 
 | 152 | 	 * Generic PCIe unit setup. | 
 | 153 | 	 */ | 
 | 154 | 	orion_pcie_set_local_bus_nr(pp->base, sys->busnr); | 
 | 155 | 	orion_pcie_setup(pp->base, &mv78xx0_mbus_dram_info); | 
 | 156 |  | 
 | 157 | 	sys->resource[0] = &pp->res[0]; | 
 | 158 | 	sys->resource[1] = &pp->res[1]; | 
 | 159 | 	sys->resource[2] = NULL; | 
 | 160 |  | 
 | 161 | 	return 1; | 
 | 162 | } | 
 | 163 |  | 
 | 164 | static struct pcie_port *bus_to_port(int bus) | 
 | 165 | { | 
 | 166 | 	int i; | 
 | 167 |  | 
 | 168 | 	for (i = num_pcie_ports - 1; i >= 0; i--) { | 
 | 169 | 		int rbus = pcie_port[i].root_bus_nr; | 
 | 170 | 		if (rbus != -1 && rbus <= bus) | 
 | 171 | 			break; | 
 | 172 | 	} | 
 | 173 |  | 
 | 174 | 	return i >= 0 ? pcie_port + i : NULL; | 
 | 175 | } | 
 | 176 |  | 
 | 177 | static int pcie_valid_config(struct pcie_port *pp, int bus, int dev) | 
 | 178 | { | 
 | 179 | 	/* | 
 | 180 | 	 * Don't go out when trying to access nonexisting devices | 
 | 181 | 	 * on the local bus. | 
 | 182 | 	 */ | 
 | 183 | 	if (bus == pp->root_bus_nr && dev > 1) | 
 | 184 | 		return 0; | 
 | 185 |  | 
 | 186 | 	return 1; | 
 | 187 | } | 
 | 188 |  | 
 | 189 | static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, | 
 | 190 | 			int size, u32 *val) | 
 | 191 | { | 
 | 192 | 	struct pcie_port *pp = bus_to_port(bus->number); | 
 | 193 | 	unsigned long flags; | 
 | 194 | 	int ret; | 
 | 195 |  | 
 | 196 | 	if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) { | 
 | 197 | 		*val = 0xffffffff; | 
 | 198 | 		return PCIBIOS_DEVICE_NOT_FOUND; | 
 | 199 | 	} | 
 | 200 |  | 
 | 201 | 	spin_lock_irqsave(&pp->conf_lock, flags); | 
 | 202 | 	ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val); | 
 | 203 | 	spin_unlock_irqrestore(&pp->conf_lock, flags); | 
 | 204 |  | 
 | 205 | 	return ret; | 
 | 206 | } | 
 | 207 |  | 
 | 208 | static int pcie_wr_conf(struct pci_bus *bus, u32 devfn, | 
 | 209 | 			int where, int size, u32 val) | 
 | 210 | { | 
 | 211 | 	struct pcie_port *pp = bus_to_port(bus->number); | 
 | 212 | 	unsigned long flags; | 
 | 213 | 	int ret; | 
 | 214 |  | 
 | 215 | 	if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) | 
 | 216 | 		return PCIBIOS_DEVICE_NOT_FOUND; | 
 | 217 |  | 
 | 218 | 	spin_lock_irqsave(&pp->conf_lock, flags); | 
 | 219 | 	ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val); | 
 | 220 | 	spin_unlock_irqrestore(&pp->conf_lock, flags); | 
 | 221 |  | 
 | 222 | 	return ret; | 
 | 223 | } | 
 | 224 |  | 
 | 225 | static struct pci_ops pcie_ops = { | 
 | 226 | 	.read = pcie_rd_conf, | 
 | 227 | 	.write = pcie_wr_conf, | 
 | 228 | }; | 
 | 229 |  | 
 | 230 | static void __devinit rc_pci_fixup(struct pci_dev *dev) | 
 | 231 | { | 
 | 232 | 	/* | 
 | 233 | 	 * Prevent enumeration of root complex. | 
 | 234 | 	 */ | 
 | 235 | 	if (dev->bus->parent == NULL && dev->devfn == 0) { | 
 | 236 | 		int i; | 
 | 237 |  | 
 | 238 | 		for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | 
 | 239 | 			dev->resource[i].start = 0; | 
 | 240 | 			dev->resource[i].end   = 0; | 
 | 241 | 			dev->resource[i].flags = 0; | 
 | 242 | 		} | 
 | 243 | 	} | 
 | 244 | } | 
 | 245 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); | 
 | 246 |  | 
 | 247 | static struct pci_bus __init * | 
 | 248 | mv78xx0_pcie_scan_bus(int nr, struct pci_sys_data *sys) | 
 | 249 | { | 
 | 250 | 	struct pci_bus *bus; | 
 | 251 |  | 
 | 252 | 	if (nr < num_pcie_ports) { | 
 | 253 | 		bus = pci_scan_bus(sys->busnr, &pcie_ops, sys); | 
 | 254 | 	} else { | 
 | 255 | 		bus = NULL; | 
 | 256 | 		BUG(); | 
 | 257 | 	} | 
 | 258 |  | 
 | 259 | 	return bus; | 
 | 260 | } | 
 | 261 |  | 
 | 262 | static int __init mv78xx0_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | 
 | 263 | { | 
 | 264 | 	struct pcie_port *pp = bus_to_port(dev->bus->number); | 
 | 265 |  | 
 | 266 | 	return IRQ_MV78XX0_PCIE_00 + (pp->maj << 2) + pp->min; | 
 | 267 | } | 
 | 268 |  | 
 | 269 | static struct hw_pci mv78xx0_pci __initdata = { | 
 | 270 | 	.nr_controllers	= 8, | 
 | 271 | 	.preinit	= mv78xx0_pcie_preinit, | 
 | 272 | 	.swizzle	= pci_std_swizzle, | 
 | 273 | 	.setup		= mv78xx0_pcie_setup, | 
 | 274 | 	.scan		= mv78xx0_pcie_scan_bus, | 
 | 275 | 	.map_irq	= mv78xx0_pcie_map_irq, | 
 | 276 | }; | 
 | 277 |  | 
 | 278 | static void __init add_pcie_port(int maj, int min, unsigned long base) | 
 | 279 | { | 
 | 280 | 	printk(KERN_INFO "MV78xx0 PCIe port %d.%d: ", maj, min); | 
 | 281 |  | 
 | 282 | 	if (orion_pcie_link_up((void __iomem *)base)) { | 
 | 283 | 		struct pcie_port *pp = &pcie_port[num_pcie_ports++]; | 
 | 284 |  | 
 | 285 | 		printk("link up\n"); | 
 | 286 |  | 
 | 287 | 		pp->maj = maj; | 
 | 288 | 		pp->min = min; | 
 | 289 | 		pp->root_bus_nr = -1; | 
 | 290 | 		pp->base = (void __iomem *)base; | 
 | 291 | 		spin_lock_init(&pp->conf_lock); | 
 | 292 | 		memset(pp->res, 0, sizeof(pp->res)); | 
 | 293 | 	} else { | 
 | 294 | 		printk("link down, ignoring\n"); | 
 | 295 | 	} | 
 | 296 | } | 
 | 297 |  | 
 | 298 | void __init mv78xx0_pcie_init(int init_port0, int init_port1) | 
 | 299 | { | 
 | 300 | 	if (init_port0) { | 
 | 301 | 		add_pcie_port(0, 0, PCIE00_VIRT_BASE); | 
 | 302 | 		if (!orion_pcie_x4_mode((void __iomem *)PCIE00_VIRT_BASE)) { | 
 | 303 | 			add_pcie_port(0, 1, PCIE01_VIRT_BASE); | 
 | 304 | 			add_pcie_port(0, 2, PCIE02_VIRT_BASE); | 
 | 305 | 			add_pcie_port(0, 3, PCIE03_VIRT_BASE); | 
 | 306 | 		} | 
 | 307 | 	} | 
 | 308 |  | 
 | 309 | 	if (init_port1) { | 
 | 310 | 		add_pcie_port(1, 0, PCIE10_VIRT_BASE); | 
 | 311 | 		if (!orion_pcie_x4_mode((void __iomem *)PCIE10_VIRT_BASE)) { | 
 | 312 | 			add_pcie_port(1, 1, PCIE11_VIRT_BASE); | 
 | 313 | 			add_pcie_port(1, 2, PCIE12_VIRT_BASE); | 
 | 314 | 			add_pcie_port(1, 3, PCIE13_VIRT_BASE); | 
 | 315 | 		} | 
 | 316 | 	} | 
 | 317 |  | 
 | 318 | 	pci_common_init(&mv78xx0_pci); | 
 | 319 | } |