diff options
Diffstat (limited to 'arch/sh/drivers/pci/pcie-sh7786.c')
| -rw-r--r-- | arch/sh/drivers/pci/pcie-sh7786.c | 205 | 
1 files changed, 138 insertions, 67 deletions
diff --git a/arch/sh/drivers/pci/pcie-sh7786.c b/arch/sh/drivers/pci/pcie-sh7786.c index ac37ee879ba..ae91a2dd918 100644 --- a/arch/sh/drivers/pci/pcie-sh7786.c +++ b/arch/sh/drivers/pci/pcie-sh7786.c @@ -1,7 +1,7 @@  /*   * Low-Level PCI Express Support for the SH7786   * - *  Copyright (C) 2009  Paul Mundt + *  Copyright (C) 2009 - 2010  Paul Mundt   *   * This file is subject to the terms and conditions of the GNU General Public   * License.  See the file "COPYING" in the main directory of this archive @@ -30,60 +30,84 @@ static struct sh7786_pcie_hwops {  	int (*port_init_hw)(struct sh7786_pcie_port *port);  } *sh7786_pcie_hwops; -static struct resource sh7786_pci_32bit_mem_resources[] = { +static struct resource sh7786_pci0_resources[] = {  	{ -		.name	= "pci0_mem", -		.start	= SH4A_PCIMEM_BASEA, -		.end	= SH4A_PCIMEM_BASEA + SZ_64M - 1, -		.flags	= IORESOURCE_MEM, +		.name	= "PCIe0 IO", +		.start	= 0xfd000000, +		.end	= 0xfd000000 + SZ_8M - 1, +		.flags	= IORESOURCE_IO,  	}, { -		.name	= "pci1_mem", -		.start	= SH4A_PCIMEM_BASEA1, -		.end	= SH4A_PCIMEM_BASEA1 + SZ_64M - 1, -		.flags	= IORESOURCE_MEM, +		.name	= "PCIe0 MEM 0", +		.start	= 0xc0000000, +		.end	= 0xc0000000 + SZ_512M - 1, +		.flags	= IORESOURCE_MEM | IORESOURCE_MEM_32BIT,  	}, { -		.name	= "pci2_mem", -		.start	= SH4A_PCIMEM_BASEA2, -		.end	= SH4A_PCIMEM_BASEA2 + SZ_64M - 1, +		.name	= "PCIe0 MEM 1", +		.start	= 0x10000000, +		.end	= 0x10000000 + SZ_64M - 1,  		.flags	= IORESOURCE_MEM, +	}, { +		.name	= "PCIe0 MEM 2", +		.start	= 0xfe100000, +		.end	= 0xfe100000 + SZ_1M - 1,  	},  }; -static struct resource sh7786_pci_29bit_mem_resource = { -	.start	= SH4A_PCIMEM_BASE, -	.end	= SH4A_PCIMEM_BASE + SZ_64M - 1, -	.flags	= IORESOURCE_MEM, +static struct resource sh7786_pci1_resources[] = { +	{ +		.name	= "PCIe1 IO", +		.start	= 0xfd800000, +		.end	= 0xfd800000 + SZ_8M - 1, +		.flags	= IORESOURCE_IO, +	}, { +		.name	= "PCIe1 MEM 0", +		.start	= 0xa0000000, +		.end	= 0xa0000000 + SZ_512M - 1, +		.flags	= IORESOURCE_MEM | IORESOURCE_MEM_32BIT, +	}, { +		.name	= "PCIe1 MEM 1", +		.start	= 0x30000000, +		.end	= 0x30000000 + SZ_256M - 1, +		.flags	= IORESOURCE_MEM | IORESOURCE_MEM_32BIT, +	}, { +		.name	= "PCIe1 MEM 2", +		.start	= 0xfe300000, +		.end	= 0xfe300000 + SZ_1M - 1, +	},  }; -static struct resource sh7786_pci_io_resources[] = { +static struct resource sh7786_pci2_resources[] = {  	{ -		.name	= "pci0_io", -		.start	= SH4A_PCIIO_BASE, -		.end	= SH4A_PCIIO_BASE + SZ_8M - 1, -		.flags	= IORESOURCE_IO, +		.name	= "PCIe2 IO", +		.start	= 0xfc800000, +		.end	= 0xfc800000 + SZ_4M - 1,  	}, { -		.name	= "pci1_io", -		.start	= SH4A_PCIIO_BASE1, -		.end	= SH4A_PCIIO_BASE1 + SZ_8M - 1, -		.flags	= IORESOURCE_IO, +		.name	= "PCIe2 MEM 0", +		.start	= 0x80000000, +		.end	= 0x80000000 + SZ_512M - 1, +		.flags	= IORESOURCE_MEM | IORESOURCE_MEM_32BIT,  	}, { -		.name	= "pci2_io", -		.start	= SH4A_PCIIO_BASE2, -		.end	= SH4A_PCIIO_BASE2 + SZ_4M - 1, -		.flags	= IORESOURCE_IO, +		.name	= "PCIe2 MEM 1", +		.start	= 0x20000000, +		.end	= 0x20000000 + SZ_256M - 1, +		.flags	= IORESOURCE_MEM | IORESOURCE_MEM_32BIT, +	}, { +		.name	= "PCIe2 MEM 2", +		.start	= 0xfcd00000, +		.end	= 0xfcd00000 + SZ_1M - 1,  	},  };  extern struct pci_ops sh7786_pci_ops; -#define DEFINE_CONTROLLER(start, idx)				\ -{								\ -	.pci_ops	= &sh7786_pci_ops,			\ -	.reg_base	= start,				\ -	/* mem_resource filled in at probe time */		\ -	.mem_offset	= 0,					\ -	.io_resource	= &sh7786_pci_io_resources[idx],	\ -	.io_offset	= 0,					\ +#define DEFINE_CONTROLLER(start, idx)					\ +{									\ +	.pci_ops	= &sh7786_pci_ops,				\ +	.resources	= sh7786_pci##idx##_resources,			\ +	.nr_resources	= ARRAY_SIZE(sh7786_pci##idx##_resources),	\ +	.reg_base	= start,					\ +	.mem_offset	= 0,						\ +	.io_offset	= 0,						\  }  static struct pci_channel sh7786_pci_channels[] = { @@ -180,7 +204,9 @@ static int pcie_init(struct sh7786_pcie_port *port)  {  	struct pci_channel *chan = port->hose;  	unsigned int data; -	int ret; +	phys_addr_t memphys; +	size_t memsize; +	int ret, i;  	/* Begin initialization */  	pci_write_reg(chan, 0, SH4A_PCIETCTLR); @@ -203,15 +229,24 @@ static int pcie_init(struct sh7786_pcie_port *port)  	data |= PCI_CAP_ID_EXP;  	pci_write_reg(chan, data, SH4A_PCIEEXPCAP0); -	/* Enable x4 link width and extended sync. */ +	/* Enable data link layer active state reporting */ +	pci_write_reg(chan, PCI_EXP_LNKCAP_DLLLARC, SH4A_PCIEEXPCAP3); + +	/* Enable extended sync and ASPM L0s support */  	data = pci_read_reg(chan, SH4A_PCIEEXPCAP4); -	data &= ~(PCI_EXP_LNKSTA_NLW << 16); -	data |= (1 << 22) | PCI_EXP_LNKCTL_ES; +	data &= ~PCI_EXP_LNKCTL_ASPMC; +	data |= PCI_EXP_LNKCTL_ES | 1;  	pci_write_reg(chan, data, SH4A_PCIEEXPCAP4); +	/* Write out the physical slot number */ +	data = pci_read_reg(chan, SH4A_PCIEEXPCAP5); +	data &= ~PCI_EXP_SLTCAP_PSN; +	data |= (port->index + 1) << 19; +	pci_write_reg(chan, data, SH4A_PCIEEXPCAP5); +  	/* Set the completion timer timeout to the maximum 32ms. */  	data = pci_read_reg(chan, SH4A_PCIETLCTLR); -	data &= ~0xffff; +	data &= ~0x3f00;  	data |= 0x32 << 8;  	pci_write_reg(chan, data, SH4A_PCIETLCTLR); @@ -224,6 +259,33 @@ static int pcie_init(struct sh7786_pcie_port *port)  	data |= (0xff << 16);  	pci_write_reg(chan, data, SH4A_PCIEMACCTLR); +	memphys = __pa(memory_start); +	memsize = roundup_pow_of_two(memory_end - memory_start); + +	/* +	 * If there's more than 512MB of memory, we need to roll over to +	 * LAR1/LAMR1. +	 */ +	if (memsize > SZ_512M) { +		__raw_writel(memphys + SZ_512M, chan->reg_base + SH4A_PCIELAR1); +		__raw_writel(((memsize - SZ_512M) - SZ_256) | 1, +			     chan->reg_base + SH4A_PCIELAMR1); +		memsize = SZ_512M; +	} else { +		/* +		 * Otherwise just zero it out and disable it. +		 */ +		__raw_writel(0, chan->reg_base + SH4A_PCIELAR1); +		__raw_writel(0, chan->reg_base + SH4A_PCIELAMR1); +	} + +	/* +	 * LAR0/LAMR0 covers up to the first 512MB, which is enough to +	 * cover all of lowmem on most platforms. +	 */ +	__raw_writel(memphys, chan->reg_base + SH4A_PCIELAR0); +	__raw_writel((memsize - SZ_256) | 1, chan->reg_base + SH4A_PCIELAMR0); +  	/* Finish initialization */  	data = pci_read_reg(chan, SH4A_PCIETCTLR);  	data |= 0x1; @@ -243,10 +305,14 @@ static int pcie_init(struct sh7786_pcie_port *port)  	if (unlikely(ret != 0))  		return -ENODEV; -	pci_write_reg(chan, 0x00100007, SH4A_PCIEPCICONF1); +	data = pci_read_reg(chan, SH4A_PCIEPCICONF1); +	data &= ~(PCI_STATUS_DEVSEL_MASK << 16); +	data |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | +		(PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST) << 16; +	pci_write_reg(chan, data, SH4A_PCIEPCICONF1); +  	pci_write_reg(chan, 0x80888000, SH4A_PCIETXVC0DCTLR);  	pci_write_reg(chan, 0x00222000, SH4A_PCIERXVC0DCTLR); -	pci_write_reg(chan, 0x000050A0, SH4A_PCIEEXPCAP2);  	wmb(); @@ -254,15 +320,32 @@ static int pcie_init(struct sh7786_pcie_port *port)  	printk(KERN_NOTICE "PCI: PCIe#%d link width %d\n",  	       port->index, (data >> 20) & 0x3f); -	pci_write_reg(chan, 0x007c0000, SH4A_PCIEPAMR0); -	pci_write_reg(chan, 0x00000000, SH4A_PCIEPARH0); -	pci_write_reg(chan, 0x00000000, SH4A_PCIEPARL0); -	pci_write_reg(chan, 0x80000100, SH4A_PCIEPTCTLR0); -	pci_write_reg(chan, 0x03fc0000, SH4A_PCIEPAMR2); -	pci_write_reg(chan, 0x00000000, SH4A_PCIEPARH2); -	pci_write_reg(chan, 0x00000000, SH4A_PCIEPARL2); -	pci_write_reg(chan, 0x80000000, SH4A_PCIEPTCTLR2); +	for (i = 0; i < chan->nr_resources; i++) { +		struct resource *res = chan->resources + i; +		resource_size_t size; +		u32 enable_mask; + +		pci_write_reg(chan, 0x00000000, SH4A_PCIEPTCTLR(i)); + +		size = resource_size(res); + +		/* +		 * The PAMR mask is calculated in units of 256kB, which +		 * keeps things pretty simple. +		 */ +		__raw_writel(((roundup_pow_of_two(size) / SZ_256K) - 1) << 18, +			     chan->reg_base + SH4A_PCIEPAMR(i)); + +		pci_write_reg(chan, 0x00000000, SH4A_PCIEPARH(i)); +		pci_write_reg(chan, 0x00000000, SH4A_PCIEPARL(i)); + +		enable_mask = MASK_PARE; +		if (res->flags & IORESOURCE_IO) +			enable_mask |= MASK_SPC; + +		pci_write_reg(chan, enable_mask, SH4A_PCIEPTCTLR(i)); +	}  	return 0;  } @@ -296,9 +379,7 @@ static int __devinit sh7786_pcie_init_hw(struct sh7786_pcie_port *port)  	if (unlikely(ret < 0))  		return ret; -	register_pci_controller(port->hose); - -	return 0; +	return register_pci_controller(port->hose);  }  static struct sh7786_pcie_hwops sh7786_65nm_pcie_hwops __initdata = { @@ -332,17 +413,7 @@ static int __init sh7786_pcie_init(void)  		port->index		= i;  		port->hose		= sh7786_pci_channels + i; -		port->hose->io_map_base	= port->hose->io_resource->start; - -		/* -		 * Check if we are booting in 29 or 32-bit mode -		 * -		 * 32-bit mode provides each controller with its own -		 * memory window, while 29-bit mode uses a shared one. -		 */ -		port->hose->mem_resource = test_mode_pin(MODE_PIN10) ? -			&sh7786_pci_32bit_mem_resources[i] : -			&sh7786_pci_29bit_mem_resource; +		port->hose->io_map_base	= port->hose->resources[0].start;  		ret |= sh7786_pcie_hwops->port_init_hw(port);  	}  |