1 /* 2 * fuloong2e specific PCI support. 3 * 4 * Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc. 5 * All rights reserved. 6 * Authors: Carsten Langgaard <carstenl@mips.com> 7 * Maciej W. Rozycki <macro@mips.com> 8 * 9 * Copyright (C) 2009 Lemote Inc. 10 * Author: Wu Zhangjin <wuzj@lemote.com> 11 * 12 * This program is free software; you can distribute it and/or modify it 13 * under the terms of the GNU General Public License (Version 2) as 14 * published by the Free Software Foundation. 15 */ 16 #include <linux/types.h> 17 #include <linux/pci.h> 18 #include <linux/kernel.h> 19 #include <linux/init.h> 20 21 #include <loongson.h> 22 23 #ifdef CONFIG_CS5536 24 #include <cs5536/cs5536_pci.h> 25 #include <cs5536/cs5536.h> 26 #endif 27 28 #define PCI_ACCESS_READ 0 29 #define PCI_ACCESS_WRITE 1 30 31 #define CFG_SPACE_REG(offset) \ 32 (void *)CKSEG1ADDR(LOONGSON_PCICFG_BASE | (offset)) 33 #define ID_SEL_BEGIN 11 34 #define MAX_DEV_NUM (31 - ID_SEL_BEGIN) 35 36 37 static int loongson_pcibios_config_access(unsigned char access_type, 38 struct pci_bus *bus, 39 unsigned int devfn, int where, 40 u32 *data) 41 { 42 u32 busnum = bus->number; 43 u32 addr, type; 44 u32 dummy; 45 void *addrp; 46 int device = PCI_SLOT(devfn); 47 int function = PCI_FUNC(devfn); 48 int reg = where & ~3; 49 50 if (busnum == 0) { 51 /* board-specific part,currently,only fuloong2f,yeeloong2f 52 * use CS5536, fuloong2e use via686b, gdium has no 53 * south bridge 54 */ 55 #ifdef CONFIG_CS5536 56 /* cs5536_pci_conf_read4/write4() will call _rdmsr/_wrmsr() to 57 * access the regsters PCI_MSR_ADDR, PCI_MSR_DATA_LO, 58 * PCI_MSR_DATA_HI, which is bigger than PCI_MSR_CTRL, so, it 59 * will not go this branch, but the others. so, no calling dead 60 * loop here. 61 */ 62 if ((PCI_IDSEL_CS5536 == device) && (reg < PCI_MSR_CTRL)) { 63 switch (access_type) { 64 case PCI_ACCESS_READ: 65 *data = cs5536_pci_conf_read4(function, reg); 66 break; 67 case PCI_ACCESS_WRITE: 68 cs5536_pci_conf_write4(function, reg, *data); 69 break; 70 } 71 return 0; 72 } 73 #endif 74 /* Type 0 configuration for onboard PCI bus */ 75 if (device > MAX_DEV_NUM) 76 return -1; 77 78 addr = (1 << (device + ID_SEL_BEGIN)) | (function << 8) | reg; 79 type = 0; 80 } else { 81 /* Type 1 configuration for offboard PCI bus */ 82 addr = (busnum << 16) | (device << 11) | (function << 8) | reg; 83 type = 0x10000; 84 } 85 86 /* Clear aborts */ 87 LOONGSON_PCICMD |= LOONGSON_PCICMD_MABORT_CLR | \ 88 LOONGSON_PCICMD_MTABORT_CLR; 89 90 LOONGSON_PCIMAP_CFG = (addr >> 16) | type; 91 92 /* Flush Bonito register block */ 93 dummy = LOONGSON_PCIMAP_CFG; 94 mmiowb(); 95 96 addrp = CFG_SPACE_REG(addr & 0xffff); 97 if (access_type == PCI_ACCESS_WRITE) 98 writel(cpu_to_le32(*data), addrp); 99 else 100 *data = le32_to_cpu(readl(addrp)); 101 102 /* Detect Master/Target abort */ 103 if (LOONGSON_PCICMD & (LOONGSON_PCICMD_MABORT_CLR | 104 LOONGSON_PCICMD_MTABORT_CLR)) { 105 /* Error occurred */ 106 107 /* Clear bits */ 108 LOONGSON_PCICMD |= (LOONGSON_PCICMD_MABORT_CLR | 109 LOONGSON_PCICMD_MTABORT_CLR); 110 111 return -1; 112 } 113 114 return 0; 115 116 } 117 118 119 /* 120 * We can't address 8 and 16 bit words directly. Instead we have to 121 * read/write a 32bit word and mask/modify the data we actually want. 122 */ 123 static int loongson_pcibios_read(struct pci_bus *bus, unsigned int devfn, 124 int where, int size, u32 *val) 125 { 126 u32 data = 0; 127 128 if ((size == 2) && (where & 1)) 129 return PCIBIOS_BAD_REGISTER_NUMBER; 130 else if ((size == 4) && (where & 3)) 131 return PCIBIOS_BAD_REGISTER_NUMBER; 132 133 if (loongson_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where, 134 &data)) 135 return -1; 136 137 if (size == 1) 138 *val = (data >> ((where & 3) << 3)) & 0xff; 139 else if (size == 2) 140 *val = (data >> ((where & 3) << 3)) & 0xffff; 141 else 142 *val = data; 143 144 return PCIBIOS_SUCCESSFUL; 145 } 146 147 static int loongson_pcibios_write(struct pci_bus *bus, unsigned int devfn, 148 int where, int size, u32 val) 149 { 150 u32 data = 0; 151 152 if ((size == 2) && (where & 1)) 153 return PCIBIOS_BAD_REGISTER_NUMBER; 154 else if ((size == 4) && (where & 3)) 155 return PCIBIOS_BAD_REGISTER_NUMBER; 156 157 if (size == 4) 158 data = val; 159 else { 160 if (loongson_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, 161 where, &data)) 162 return -1; 163 164 if (size == 1) 165 data = (data & ~(0xff << ((where & 3) << 3))) | 166 (val << ((where & 3) << 3)); 167 else if (size == 2) 168 data = (data & ~(0xffff << ((where & 3) << 3))) | 169 (val << ((where & 3) << 3)); 170 } 171 172 if (loongson_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, where, 173 &data)) 174 return -1; 175 176 return PCIBIOS_SUCCESSFUL; 177 } 178 179 struct pci_ops loongson_pci_ops = { 180 .read = loongson_pcibios_read, 181 .write = loongson_pcibios_write 182 }; 183 184 #ifdef CONFIG_CS5536 185 void _rdmsr(u32 msr, u32 *hi, u32 *lo) 186 { 187 struct pci_bus bus = { 188 .number = PCI_BUS_CS5536 189 }; 190 u32 devfn = PCI_DEVFN(PCI_IDSEL_CS5536, 0); 191 loongson_pcibios_write(&bus, devfn, PCI_MSR_ADDR, 4, msr); 192 loongson_pcibios_read(&bus, devfn, PCI_MSR_DATA_LO, 4, lo); 193 loongson_pcibios_read(&bus, devfn, PCI_MSR_DATA_HI, 4, hi); 194 } 195 EXPORT_SYMBOL(_rdmsr); 196 197 void _wrmsr(u32 msr, u32 hi, u32 lo) 198 { 199 struct pci_bus bus = { 200 .number = PCI_BUS_CS5536 201 }; 202 u32 devfn = PCI_DEVFN(PCI_IDSEL_CS5536, 0); 203 loongson_pcibios_write(&bus, devfn, PCI_MSR_ADDR, 4, msr); 204 loongson_pcibios_write(&bus, devfn, PCI_MSR_DATA_LO, 4, lo); 205 loongson_pcibios_write(&bus, devfn, PCI_MSR_DATA_HI, 4, hi); 206 } 207 EXPORT_SYMBOL(_wrmsr); 208 #endif 209