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