| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * BRIEF MODULE DESCRIPTION | 
 | 3 |  *	Alchemy/AMD Au1x00 pci support. | 
 | 4 |  * | 
 | 5 |  * Copyright 2001,2002,2003 MontaVista Software Inc. | 
 | 6 |  * Author: MontaVista Software, Inc. | 
 | 7 |  *         	ppopov@mvista.com or source@mvista.com | 
 | 8 |  * | 
 | 9 |  *  Support for all devices (greater than 16) added by David Gathright. | 
 | 10 |  * | 
 | 11 |  *  This program is free software; you can redistribute  it and/or modify it | 
 | 12 |  *  under  the terms of  the GNU General  Public License as published by the | 
 | 13 |  *  Free Software Foundation;  either version 2 of the  License, or (at your | 
 | 14 |  *  option) any later version. | 
 | 15 |  * | 
 | 16 |  *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED | 
 | 17 |  *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF | 
 | 18 |  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN | 
 | 19 |  *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT, | 
 | 20 |  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
 | 21 |  *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF | 
 | 22 |  *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | 
 | 23 |  *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT | 
 | 24 |  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
 | 25 |  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 | 26 |  * | 
 | 27 |  *  You should have received a copy of the  GNU General Public License along | 
 | 28 |  *  with this program; if not, write  to the Free Software Foundation, Inc., | 
 | 29 |  *  675 Mass Ave, Cambridge, MA 02139, USA. | 
 | 30 |  */ | 
 | 31 | #include <linux/config.h> | 
 | 32 | #include <linux/types.h> | 
 | 33 | #include <linux/pci.h> | 
 | 34 | #include <linux/kernel.h> | 
 | 35 | #include <linux/init.h> | 
 | 36 | #include <linux/vmalloc.h> | 
 | 37 |  | 
 | 38 | #include <asm/mach-au1x00/au1000.h> | 
 | 39 |  | 
 | 40 | #undef DEBUG | 
 | 41 | #ifdef DEBUG | 
 | 42 | #define DBG(x...) printk(x) | 
 | 43 | #else | 
 | 44 | #define DBG(x...) | 
 | 45 | #endif | 
 | 46 |  | 
 | 47 | #define PCI_ACCESS_READ  0 | 
 | 48 | #define PCI_ACCESS_WRITE 1 | 
 | 49 |  | 
 | 50 |  | 
 | 51 | int (*board_pci_idsel)(unsigned int devsel, int assert); | 
 | 52 |  | 
 | 53 | /* CP0 hazard avoidance. */ | 
 | 54 | #define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ | 
 | 55 | 				     "nop; nop; nop; nop;\t" \ | 
 | 56 | 				     ".set reorder\n\t") | 
 | 57 |  | 
 | 58 | void mod_wired_entry(int entry, unsigned long entrylo0, | 
 | 59 | 		unsigned long entrylo1, unsigned long entryhi, | 
 | 60 | 		unsigned long pagemask) | 
 | 61 | { | 
 | 62 | 	unsigned long old_pagemask; | 
 | 63 | 	unsigned long old_ctx; | 
 | 64 |  | 
 | 65 | 	/* Save old context and create impossible VPN2 value */ | 
 | 66 | 	old_ctx = read_c0_entryhi() & 0xff; | 
 | 67 | 	old_pagemask = read_c0_pagemask(); | 
 | 68 | 	write_c0_index(entry); | 
 | 69 | 	BARRIER; | 
 | 70 | 	write_c0_pagemask(pagemask); | 
 | 71 | 	write_c0_entryhi(entryhi); | 
 | 72 | 	write_c0_entrylo0(entrylo0); | 
 | 73 | 	write_c0_entrylo1(entrylo1); | 
 | 74 | 	BARRIER; | 
 | 75 | 	tlb_write_indexed(); | 
 | 76 | 	BARRIER; | 
 | 77 | 	write_c0_entryhi(old_ctx); | 
 | 78 | 	BARRIER; | 
 | 79 | 	write_c0_pagemask(old_pagemask); | 
 | 80 | } | 
 | 81 |  | 
 | 82 | struct vm_struct *pci_cfg_vm; | 
 | 83 | static int pci_cfg_wired_entry; | 
 | 84 | static int first_cfg = 1; | 
 | 85 | unsigned long last_entryLo0, last_entryLo1; | 
 | 86 |  | 
 | 87 | static int config_access(unsigned char access_type, struct pci_bus *bus, | 
 | 88 | 			 unsigned int dev_fn, unsigned char where, | 
 | 89 | 			 u32 * data) | 
 | 90 | { | 
 | 91 | #if defined( CONFIG_SOC_AU1500 ) || defined( CONFIG_SOC_AU1550 ) | 
 | 92 | 	unsigned int device = PCI_SLOT(dev_fn); | 
 | 93 | 	unsigned int function = PCI_FUNC(dev_fn); | 
 | 94 | 	unsigned long offset, status; | 
 | 95 | 	unsigned long cfg_base; | 
 | 96 | 	unsigned long flags; | 
 | 97 | 	int error = PCIBIOS_SUCCESSFUL; | 
 | 98 | 	unsigned long entryLo0, entryLo1; | 
 | 99 |  | 
 | 100 | 	if (device > 19) { | 
 | 101 | 		*data = 0xffffffff; | 
 | 102 | 		return -1; | 
 | 103 | 	} | 
 | 104 |  | 
 | 105 | 	local_irq_save(flags); | 
 | 106 | 	au_writel(((0x2000 << 16) | (au_readl(Au1500_PCI_STATCMD) & 0xffff)), | 
 | 107 | 			Au1500_PCI_STATCMD); | 
 | 108 | 	au_sync_udelay(1); | 
 | 109 |  | 
 | 110 | 	/* | 
 | 111 | 	 * We can't ioremap the entire pci config space because it's | 
 | 112 | 	 * too large. Nor can we call ioremap dynamically because some | 
 | 113 | 	 * device drivers use the pci config routines from within | 
 | 114 | 	 * interrupt handlers and that becomes a problem in get_vm_area(). | 
 | 115 | 	 * We use one wired tlb to handle all config accesses for all | 
 | 116 | 	 * busses. To improve performance, if the current device | 
 | 117 | 	 * is the same as the last device accessed, we don't touch the | 
 | 118 | 	 * tlb. | 
 | 119 | 	 */ | 
 | 120 | 	if (first_cfg) { | 
 | 121 | 		/* reserve a wired entry for pci config accesses */ | 
 | 122 | 		first_cfg = 0; | 
 | 123 | 		pci_cfg_vm = get_vm_area(0x2000, 0); | 
 | 124 | 		if (!pci_cfg_vm) | 
 | 125 | 			panic (KERN_ERR "PCI unable to get vm area\n"); | 
 | 126 | 		pci_cfg_wired_entry = read_c0_wired(); | 
 | 127 | 		add_wired_entry(0, 0, (unsigned long)pci_cfg_vm->addr, PM_4K); | 
 | 128 | 		last_entryLo0  = last_entryLo1 = 0xffffffff; | 
 | 129 | 	} | 
 | 130 |  | 
 | 131 | 	/* Since the Au1xxx doesn't do the idsel timing exactly to spec, | 
 | 132 | 	 * many board vendors implement their own off-chip idsel, so call | 
 | 133 | 	 * it now.  If it doesn't succeed, may as well bail out at this point. | 
 | 134 | 	 */ | 
 | 135 | 	if (board_pci_idsel) { | 
 | 136 | 		if (board_pci_idsel(device, 1) == 0) { | 
 | 137 | 			*data = 0xffffffff; | 
 | 138 | 			local_irq_restore(flags); | 
 | 139 | 			return -1; | 
 | 140 | 		} | 
 | 141 | 	} | 
 | 142 |  | 
 | 143 |         /* setup the config window */ | 
 | 144 |         if (bus->number == 0) { | 
 | 145 |                 cfg_base = ((1<<device)<<11); | 
 | 146 |         } else { | 
 | 147 |                 cfg_base = 0x80000000 | (bus->number<<16) | (device<<11); | 
 | 148 |         } | 
 | 149 |  | 
 | 150 |         /* setup the lower bits of the 36 bit address */ | 
 | 151 |         offset = (function << 8) | (where & ~0x3); | 
 | 152 | 	/* pick up any address that falls below the page mask */ | 
 | 153 | 	offset |= cfg_base & ~PAGE_MASK; | 
 | 154 |  | 
 | 155 | 	/* page boundary */ | 
 | 156 | 	cfg_base = cfg_base & PAGE_MASK; | 
 | 157 |  | 
 | 158 | 	entryLo0 = (6 << 26)  | (cfg_base >> 6) | (2 << 3) | 7; | 
 | 159 | 	entryLo1 = (6 << 26)  | (cfg_base >> 6) | (0x1000 >> 6) | (2 << 3) | 7; | 
 | 160 |  | 
 | 161 | 	if ((entryLo0 != last_entryLo0) || (entryLo1 != last_entryLo1)) { | 
 | 162 | 		mod_wired_entry(pci_cfg_wired_entry, entryLo0, entryLo1, | 
 | 163 | 				(unsigned long)pci_cfg_vm->addr, PM_4K); | 
 | 164 | 		last_entryLo0 = entryLo0; | 
 | 165 | 		last_entryLo1 = entryLo1; | 
 | 166 | 	} | 
 | 167 |  | 
 | 168 | 	if (access_type == PCI_ACCESS_WRITE) { | 
 | 169 | 		au_writel(*data, (int)(pci_cfg_vm->addr + offset)); | 
 | 170 | 	} else { | 
 | 171 | 		*data = au_readl((int)(pci_cfg_vm->addr + offset)); | 
 | 172 | 	} | 
 | 173 | 	au_sync_udelay(2); | 
 | 174 |  | 
 | 175 | 	DBG("cfg_access %d bus->number %d dev %d at %x *data %x conf %x\n", | 
 | 176 | 			access_type, bus->number, device, where, *data, offset); | 
 | 177 |  | 
 | 178 | 	/* check master abort */ | 
 | 179 | 	status = au_readl(Au1500_PCI_STATCMD); | 
 | 180 |  | 
 | 181 | 	if (status & (1<<29)) { | 
 | 182 | 		*data = 0xffffffff; | 
 | 183 | 		error = -1; | 
 | 184 | 		DBG("Au1x Master Abort\n"); | 
 | 185 | 	} else if ((status >> 28) & 0xf) { | 
 | 186 | 		DBG("PCI ERR detected: status %x\n", status); | 
 | 187 | 		*data = 0xffffffff; | 
 | 188 | 		error = -1; | 
 | 189 | 	} | 
 | 190 |  | 
 | 191 | 	/* Take away the idsel. | 
 | 192 | 	*/ | 
 | 193 | 	if (board_pci_idsel) { | 
 | 194 | 		(void)board_pci_idsel(device, 0); | 
 | 195 | 	} | 
 | 196 |  | 
 | 197 | 	local_irq_restore(flags); | 
 | 198 | 	return error; | 
 | 199 | #endif | 
 | 200 | } | 
 | 201 |  | 
 | 202 | static int read_config_byte(struct pci_bus *bus, unsigned int devfn, | 
 | 203 | 			    int where, u8 * val) | 
 | 204 | { | 
 | 205 | 	u32 data; | 
 | 206 | 	int ret; | 
 | 207 |  | 
 | 208 | 	ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); | 
 | 209 | 	if (where & 1) | 
 | 210 | 		data >>= 8; | 
 | 211 | 	if (where & 2) | 
 | 212 | 		data >>= 16; | 
 | 213 | 	*val = data & 0xff; | 
 | 214 | 	return ret; | 
 | 215 | } | 
 | 216 |  | 
 | 217 |  | 
 | 218 | static int read_config_word(struct pci_bus *bus, unsigned int devfn, | 
 | 219 | 			    int where, u16 * val) | 
 | 220 | { | 
 | 221 | 	u32 data; | 
 | 222 | 	int ret; | 
 | 223 |  | 
 | 224 | 	ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); | 
 | 225 | 	if (where & 2) | 
 | 226 | 		data >>= 16; | 
 | 227 | 	*val = data & 0xffff; | 
 | 228 | 	return ret; | 
 | 229 | } | 
 | 230 |  | 
 | 231 | static int read_config_dword(struct pci_bus *bus, unsigned int devfn, | 
 | 232 | 			     int where, u32 * val) | 
 | 233 | { | 
 | 234 | 	int ret; | 
 | 235 |  | 
 | 236 | 	ret = config_access(PCI_ACCESS_READ, bus, devfn, where, val); | 
 | 237 | 	return ret; | 
 | 238 | } | 
 | 239 |  | 
 | 240 | static int | 
 | 241 | write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, | 
 | 242 | 		  u8 val) | 
 | 243 | { | 
 | 244 | 	u32 data = 0; | 
 | 245 |  | 
 | 246 | 	if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) | 
 | 247 | 		return -1; | 
 | 248 |  | 
 | 249 | 	data = (data & ~(0xff << ((where & 3) << 3))) | | 
 | 250 | 	    (val << ((where & 3) << 3)); | 
 | 251 |  | 
 | 252 | 	if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) | 
 | 253 | 		return -1; | 
 | 254 |  | 
 | 255 | 	return PCIBIOS_SUCCESSFUL; | 
 | 256 | } | 
 | 257 |  | 
 | 258 | static int | 
 | 259 | write_config_word(struct pci_bus *bus, unsigned int devfn, int where, | 
 | 260 | 		  u16 val) | 
 | 261 | { | 
 | 262 | 	u32 data = 0; | 
 | 263 |  | 
 | 264 | 	if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) | 
 | 265 | 		return -1; | 
 | 266 |  | 
 | 267 | 	data = (data & ~(0xffff << ((where & 3) << 3))) | | 
 | 268 | 	    (val << ((where & 3) << 3)); | 
 | 269 |  | 
 | 270 | 	if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) | 
 | 271 | 		return -1; | 
 | 272 |  | 
 | 273 |  | 
 | 274 | 	return PCIBIOS_SUCCESSFUL; | 
 | 275 | } | 
 | 276 |  | 
 | 277 | static int | 
 | 278 | write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, | 
 | 279 | 		   u32 val) | 
 | 280 | { | 
 | 281 | 	if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &val)) | 
 | 282 | 		return -1; | 
 | 283 |  | 
 | 284 | 	return PCIBIOS_SUCCESSFUL; | 
 | 285 | } | 
 | 286 |  | 
 | 287 | static int config_read(struct pci_bus *bus, unsigned int devfn, | 
 | 288 | 		       int where, int size, u32 * val) | 
 | 289 | { | 
 | 290 | 	switch (size) { | 
 | 291 | 	case 1: { | 
 | 292 | 			u8 _val; | 
 | 293 | 			int rc = read_config_byte(bus, devfn, where, &_val); | 
 | 294 | 			*val = _val; | 
 | 295 | 			return rc; | 
 | 296 | 		} | 
 | 297 |        case 2: { | 
 | 298 | 			u16 _val; | 
 | 299 | 			int rc = read_config_word(bus, devfn, where, &_val); | 
 | 300 | 			*val = _val; | 
 | 301 | 			return rc; | 
 | 302 | 		} | 
 | 303 | 	default: | 
 | 304 | 		return read_config_dword(bus, devfn, where, val); | 
 | 305 | 	} | 
 | 306 | } | 
 | 307 |  | 
 | 308 | static int config_write(struct pci_bus *bus, unsigned int devfn, | 
 | 309 | 			int where, int size, u32 val) | 
 | 310 | { | 
 | 311 | 	switch (size) { | 
 | 312 | 	case 1: | 
 | 313 | 		return write_config_byte(bus, devfn, where, (u8) val); | 
 | 314 | 	case 2: | 
 | 315 | 		return write_config_word(bus, devfn, where, (u16) val); | 
 | 316 | 	default: | 
 | 317 | 		return write_config_dword(bus, devfn, where, val); | 
 | 318 | 	} | 
 | 319 | } | 
 | 320 |  | 
 | 321 |  | 
 | 322 | struct pci_ops au1x_pci_ops = { | 
 | 323 | 	config_read, | 
 | 324 | 	config_write | 
 | 325 | }; |