| 
									
										
										
										
											2008-04-24 19:21:10 +10:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * IO workarounds for PCI on Celleb/Cell platform | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * (C) Copyright 2006-2007 TOSHIBA CORPORATION | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License along | 
					
						
							|  |  |  |  * with this program; if not, write to the Free Software Foundation, Inc., | 
					
						
							|  |  |  |  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #undef DEBUG
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/of_platform.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/ppc-pci.h>
 | 
					
						
							|  |  |  | #include <asm/pci-bridge.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "io-workarounds.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SPIDER_PCI_DISABLE_PREFETCH
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct spiderpci_iowa_private { | 
					
						
							|  |  |  | 	void __iomem *regs; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void spiderpci_io_flush(struct iowa_bus *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct spiderpci_iowa_private *priv; | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	priv = bus->private; | 
					
						
							|  |  |  | 	val = in_be32(priv->regs + SPIDER_PCI_DUMMY_READ); | 
					
						
							|  |  |  | 	iosync(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SPIDER_PCI_MMIO_READ(name, ret)					\
 | 
					
						
							|  |  |  | static ret spiderpci_##name(const PCI_IO_ADDR addr)			\ | 
					
						
							|  |  |  | {									\ | 
					
						
							|  |  |  | 	ret val = __do_##name(addr);					\ | 
					
						
							|  |  |  | 	spiderpci_io_flush(iowa_mem_find_bus(addr));			\ | 
					
						
							|  |  |  | 	return val;							\ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SPIDER_PCI_MMIO_READ_STR(name)					\
 | 
					
						
							|  |  |  | static void spiderpci_##name(const PCI_IO_ADDR addr, void *buf, 	\ | 
					
						
							|  |  |  | 			     unsigned long count)			\ | 
					
						
							|  |  |  | {									\ | 
					
						
							|  |  |  | 	__do_##name(addr, buf, count);					\ | 
					
						
							|  |  |  | 	spiderpci_io_flush(iowa_mem_find_bus(addr));			\ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ(readb, u8) | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ(readw, u16) | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ(readl, u32) | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ(readq, u64) | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ(readw_be, u16) | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ(readl_be, u32) | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ(readq_be, u64) | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ_STR(readsb) | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ_STR(readsw) | 
					
						
							|  |  |  | SPIDER_PCI_MMIO_READ_STR(readsl) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void spiderpci_memcpy_fromio(void *dest, const PCI_IO_ADDR src, | 
					
						
							|  |  |  | 				    unsigned long n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	__do_memcpy_fromio(dest, src, n); | 
					
						
							|  |  |  | 	spiderpci_io_flush(iowa_mem_find_bus(src)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init spiderpci_pci_setup_chip(struct pci_controller *phb, | 
					
						
							|  |  |  | 					   void __iomem *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void *dummy_page_va; | 
					
						
							|  |  |  | 	dma_addr_t dummy_page_da; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef SPIDER_PCI_DISABLE_PREFETCH
 | 
					
						
							|  |  |  | 	u32 val = in_be32(regs + SPIDER_PCI_VCI_CNTL_STAT); | 
					
						
							|  |  |  | 	pr_debug("SPIDER_IOWA:PVCI_Control_Status was 0x%08x\n", val); | 
					
						
							|  |  |  | 	out_be32(regs + SPIDER_PCI_VCI_CNTL_STAT, val | 0x8); | 
					
						
							|  |  |  | #endif /* SPIDER_PCI_DISABLE_PREFETCH */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* setup dummy read */ | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * On CellBlade, we can't know that which XDR memory is used by | 
					
						
							|  |  |  | 	 * kmalloc() to allocate dummy_page_va. | 
					
						
							|  |  |  | 	 * In order to imporve the performance, the XDR which is used to | 
					
						
							|  |  |  | 	 * allocate dummy_page_va is the nearest the spider-pci. | 
					
						
							|  |  |  | 	 * We have to select the CBE which is the nearest the spider-pci | 
					
						
							|  |  |  | 	 * to allocate memory from the best XDR, but I don't know that | 
					
						
							|  |  |  | 	 * how to do. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * Celleb does not have this problem, because it has only one XDR. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!dummy_page_va) { | 
					
						
							|  |  |  | 		pr_err("SPIDERPCI-IOWA:Alloc dummy_page_va failed.\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dummy_page_da = dma_map_single(phb->parent, dummy_page_va, | 
					
						
							|  |  |  | 				       PAGE_SIZE, DMA_FROM_DEVICE); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:44:49 -07:00
										 |  |  | 	if (dma_mapping_error(phb->parent, dummy_page_da)) { | 
					
						
							| 
									
										
										
										
											2008-04-24 19:21:10 +10:00
										 |  |  | 		pr_err("SPIDER-IOWA:Map dummy page filed.\n"); | 
					
						
							|  |  |  | 		kfree(dummy_page_va); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_be32(regs + SPIDER_PCI_DUMMY_READ_BASE, dummy_page_da); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int __init spiderpci_iowa_init(struct iowa_bus *bus, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void __iomem *regs = NULL; | 
					
						
							|  |  |  | 	struct spiderpci_iowa_private *priv; | 
					
						
							|  |  |  | 	struct device_node *np = bus->phb->dn; | 
					
						
							|  |  |  | 	struct resource r; | 
					
						
							|  |  |  | 	unsigned long offset = (unsigned long)data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pr_debug("SPIDERPCI-IOWA:Bus initialize for spider(%s)\n", | 
					
						
							|  |  |  | 		 np->full_name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	priv = kzalloc(sizeof(struct spiderpci_iowa_private), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!priv) { | 
					
						
							|  |  |  | 		pr_err("SPIDERPCI-IOWA:" | 
					
						
							|  |  |  | 		       "Can't allocate struct spiderpci_iowa_private"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (of_address_to_resource(np, 0, &r)) { | 
					
						
							|  |  |  | 		pr_err("SPIDERPCI-IOWA:Can't get resource.\n"); | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	regs = ioremap(r.start + offset, SPIDER_PCI_REG_SIZE); | 
					
						
							|  |  |  | 	if (!regs) { | 
					
						
							|  |  |  | 		pr_err("SPIDERPCI-IOWA:ioremap failed.\n"); | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	priv->regs = regs; | 
					
						
							|  |  |  | 	bus->private = priv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (spiderpci_pci_setup_chip(bus->phb, regs)) | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  | 	kfree(priv); | 
					
						
							|  |  |  | 	bus->private = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (regs) | 
					
						
							|  |  |  | 		iounmap(regs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ppc_pci_io spiderpci_ops = { | 
					
						
							|  |  |  | 	.readb = spiderpci_readb, | 
					
						
							|  |  |  | 	.readw = spiderpci_readw, | 
					
						
							|  |  |  | 	.readl = spiderpci_readl, | 
					
						
							|  |  |  | 	.readq = spiderpci_readq, | 
					
						
							|  |  |  | 	.readw_be = spiderpci_readw_be, | 
					
						
							|  |  |  | 	.readl_be = spiderpci_readl_be, | 
					
						
							|  |  |  | 	.readq_be = spiderpci_readq_be, | 
					
						
							|  |  |  | 	.readsb = spiderpci_readsb, | 
					
						
							|  |  |  | 	.readsw = spiderpci_readsw, | 
					
						
							|  |  |  | 	.readsl = spiderpci_readsl, | 
					
						
							|  |  |  | 	.memcpy_fromio = spiderpci_memcpy_fromio, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 |