Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: pci_common.cc | |
4 | // Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
5 | // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
6 | // | |
7 | // The above named program is free software; you can redistribute it and/or | |
8 | // modify it under the terms of the GNU General Public | |
9 | // License version 2 as published by the Free Software Foundation. | |
10 | // | |
11 | // The above named program is distributed in the hope that it will be | |
12 | // useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | // General Public License for more details. | |
15 | // | |
16 | // You should have received a copy of the GNU General Public | |
17 | // License along with this work; if not, write to the Free Software | |
18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | // | |
20 | // ========== Copyright Header End ============================================ | |
21 | #include "pci_common.h" | |
22 | ||
23 | ||
24 | /***************************************************************************************************/ | |
25 | //pci configuration space class functions// | |
26 | ||
27 | ||
28 | //fill in the default values for register values, masks, names etc. | |
29 | void pciConfSpace::initConf(confHeaderType htype){ | |
30 | ||
31 | if(htype == pciHeaderNull){ | |
32 | // for devices that know what they want | |
33 | return; | |
34 | }else if(htype == pciHeader0){ | |
35 | ||
36 | //for now only type 0 header. type 1 support to be done | |
37 | ||
38 | //common registers for all pci devices | |
39 | //specific implementations must provide specific | |
40 | //values for mask and init value if different from default | |
41 | ||
42 | //NOTE: command and status registers are not added here since they are specialized | |
43 | //versions of pciConfReg, requiring callback data pointer. they must be initialized | |
44 | //after device instantiation and added to pci configuration space. | |
45 | addConfReg(new pciConfReg("Vendor Id", 2, 0x0, 0x0), PCI_CONF_VENID); | |
46 | addConfReg(new pciConfReg("Device id", 2, 0x0, 0x0), PCI_CONF_DEVID); | |
47 | //addConfReg(new pciCommandReg("Command", 0x3ff, this), PCI_CONF_COMM); | |
48 | //addConfReg(new pciStatusReg("Status", 0x0, 0xff90, this), PCI_CONF_STAT); | |
49 | addConfReg(new pciConfReg("Revision Id", 1, 0x0, 0x0), PCI_CONF_REVID); | |
50 | addConfReg(new pciConfReg("Programming class", 1, 0x0, 0x0), PCI_CONF_PROGCLASS); | |
51 | addConfReg(new pciConfReg("Sub class", 1, 0x0, 0x0), PCI_CONF_SUBCLASS); | |
52 | addConfReg(new pciConfReg("Base class", 1, 0x0, 0x0), PCI_CONF_BASCLASS); | |
53 | addConfReg(new pciConfReg("Cache line size", 1, 0x0, 0xff), PCI_CONF_CACHE_LINESZ); | |
54 | addConfReg(new pciConfReg("Latency time", 1, 0, 0xff), PCI_CONF_LATENCY_TIMER); | |
55 | addConfReg(new pciConfReg("Header type", 1, 0x0, 0x00), PCI_CONF_HEADER); //default type 0 | |
56 | addConfReg(new pciConfReg("BIST", 4, 0x0, 0x40), PCI_CONF_BIST); | |
57 | ||
58 | //these registers may or may not be implemented. The write mask is | |
59 | //0x0. The specific device implementation must change the mask to correct | |
60 | //value if the register is implemented and provide the correct init value. | |
61 | //in case the register is read only, provide the init value | |
62 | ||
63 | //NOTE: the base address registers(BAR) are not added here since they need the size | |
64 | //information, known only to specific device implementation. should be added during initialization. | |
65 | addConfReg(new pciConfReg("CardBus CIS pointer", 4,0x0,0x0), PCI_CONF_CIS); | |
66 | addConfReg(new pciConfReg("Subsystem vendor id", 2,0x0,0x0), PCI_CONF_SUBVENID); | |
67 | addConfReg(new pciConfReg("Subsystem Id", 2,0x0,0x0), PCI_CONF_SUBSYSID); | |
68 | addConfReg(new pciConfReg("Expansion ROM base address", 4,0x0,0x0), PCI_CONF_ROM); | |
69 | addConfReg(new pciConfReg("Capabilities pointer", 1,0x0,0x0), PCI_CONF_CAP_PTR); | |
70 | addConfReg(new pciConfReg("Interrupt line", 1,0x0,0x0), PCI_CONF_ILINE); | |
71 | addConfReg(new pciConfReg("Interrupt pin", 1,0x0,0x0), PCI_CONF_IPIN); //rdonly register | |
72 | addConfReg(new pciConfReg("Min grant", 1,0x0,0x0), PCI_CONF_MIN_G); //rdonly | |
73 | addConfReg(new pciConfReg("Min grant", 1,0x0,0x0), PCI_CONF_MAX_L); //rdonly | |
74 | ||
75 | }else{ | |
76 | printf("confspace init: header type 1 unsupported yet.\n"); | |
77 | } | |
78 | } | |
79 | ||
80 | ||
81 | ||
82 | //add a configuration register to the map. If there is a conflict | |
83 | //in address range , then the old reg(s) are removed and new is installed. | |
84 | //this allows the device models to have device specific configuration | |
85 | //registers not part of the generic PCI model. | |
86 | bool pciConfSpace::addConfReg(pciConfReg * cr, int offset){ | |
87 | ||
88 | iter_t iter_low, iter; | |
89 | if( ((iter = pciConfRegMap.lower_bound(offset)) != pciConfRegMap.end() ) && (iter->second->isHole == false) ){ | |
90 | printf("current Conf space registers-->\n");print(); | |
91 | printf("addConfReg error: conf reg at offset 0x%x already exist, name<%s>\nDeleted..\n",offset,iter->second->name); | |
92 | deleteConfReg(offset); | |
93 | //XXX corner case. inserting a larger reg than the 1 being deleted | |
94 | } | |
95 | ||
96 | cr->setDebug(debug_level); | |
97 | iter_low = pciConfRegMap.lower_bound(offset); | |
98 | ||
99 | assert(iter_low->second->isHole); | |
100 | int size_hole1 = offset - iter_low->first; | |
101 | int size_hole2 = iter_low->second->size - (size_hole1 + cr->size); | |
102 | ||
103 | if(size_hole1 <= 0) | |
104 | //hole size became 0. delete it | |
105 | pciConfRegMap.erase(iter_low->first); | |
106 | else | |
107 | iter_low->second->size = size_hole1; | |
108 | ||
109 | if(size_hole2 > 0){ | |
110 | pciConfReg * hole = new pciConfReg(size_hole2); | |
111 | pciConfRegMap[offset + cr->size] = hole; | |
112 | } | |
113 | ||
114 | pciConfRegMap[offset] = cr; | |
115 | return true; | |
116 | } | |
117 | ||
118 | bool pciConfSpace::deleteConfReg(int offset){ | |
119 | ||
120 | if( (pciConfRegMap.find(offset) == pciConfRegMap.end()) || pciConfRegMap.find(offset)->second->isHole == true ){ | |
121 | printf("deleteConfReg error: conf reg at offset 0x%x does not exist\n",offset); | |
122 | return false; | |
123 | } | |
124 | ||
125 | iter_t iter_low, iter_high, iter; | |
126 | iter = pciConfRegMap.find(offset); | |
127 | iter_low = pciConfRegMap.upper_bound(offset); | |
128 | iter_high = pciConfRegMap.lower_bound(offset); | |
129 | ||
130 | int high_size = 0, high_offset = 0; | |
131 | if(iter_high->second->isHole){ | |
132 | high_offset = iter_high->first; | |
133 | high_size = iter_high->second->size; | |
134 | pciConfRegMap.erase(iter_high->first); | |
135 | } | |
136 | ||
137 | if(iter_low->second->isHole){ | |
138 | iter_low->second->size += iter->second->size + high_size; | |
139 | pciConfRegMap.erase(offset); | |
140 | }else{ | |
141 | pciConfReg * hole = new pciConfReg(iter->second->size + high_size); | |
142 | pciConfRegMap[offset] = hole; | |
143 | } | |
144 | ||
145 | return true; | |
146 | ||
147 | } | |
148 | ||
149 | ||
150 | //read/write contiguous 'size' bytes from 'offset' and return the value in buf in case of read. | |
151 | //it is assumed that alignment restrictions are maintained, with this kind of access. | |
152 | bool pciConfSpace::confAccessSize(int offset,bool wr, uint64_t * in_buf,uint8_t size){ | |
153 | ||
154 | // assert(offset <= 0xff); move to caller XXX | |
155 | assert(size <= 4); | |
156 | ||
157 | uint64_t local_buf = *in_buf; | |
158 | uint64_t *buf = &local_buf; | |
159 | uint32_t buf_lo = local_buf; | |
160 | ||
161 | //if(offset % size != 0)//XXX move checking to caller | |
162 | // return TARGET_ABORT; | |
163 | ||
164 | iter_t iter = pciConfRegMap.lower_bound(offset); //find the element with key less than or equal | |
165 | //to offset | |
166 | assert(iter != pciConfRegMap.end()); | |
167 | ||
168 | //if(debug_level >= 2) | |
169 | // iter->second->print(); | |
170 | ||
171 | uint8_t byte_offset = offset - iter->first; | |
172 | uint8_t bytes_consumed = 0; | |
173 | uint8_t buf_offset = 0; | |
174 | while(size!=0){ | |
175 | if(iter->second->size > byte_offset){ | |
176 | if(wr){ | |
177 | bytes_consumed = iter->second->write(*buf,byte_offset,size); | |
178 | *buf >>= bytes_consumed*8; | |
179 | }else{ | |
180 | bytes_consumed = iter->second->read(&buf_lo,size,byte_offset,buf_offset); | |
181 | local_buf = ((local_buf >> 32) << 32) | buf_lo; | |
182 | buf_offset += bytes_consumed; | |
183 | //*buf <<= (size - bytes_consumed) * 8; | |
184 | } | |
185 | size -= bytes_consumed; | |
186 | byte_offset += bytes_consumed; | |
187 | }else{ | |
188 | //iter++; | |
189 | iter--; | |
190 | //if(debug_level >= 2) | |
191 | // iter->second->print(); | |
192 | byte_offset = 0; | |
193 | } | |
194 | } | |
195 | if(!wr) | |
196 | *in_buf = *buf; | |
197 | return true; | |
198 | } | |
199 | ||
200 | // access the conf space with 'be' supplying the byte enables. | |
201 | // for a read, bytes with corresponding byte enable as 1 will be meaning ful | |
202 | // for a write, only those bytes with corresponding byte as 1 will be written to. | |
203 | // the function would try to read/write contiguous bytes using a single confAccessSize() call. | |
204 | bool pciConfSpace::confAccessByteE(int offset,bool wr, uint64_t * in_buf,uint8_t be){ | |
205 | assert ((be & 0xf0) == 0); // upper 4 bits have to be 0. | |
206 | ||
207 | uint64_t local_buf = *in_buf; | |
208 | bool ret = false; | |
209 | bool to_rd_wr = false; | |
210 | int bytes_to_rd_wr = 0; | |
211 | uint64_t buf_1 = 0; | |
212 | const int orig_offset = offset; | |
213 | ||
214 | if(!wr){ | |
215 | // if a read, 0 out the bytes that have to be read | |
216 | for(int i = 0; i < 4; i++) | |
217 | if( (be >> i & 0x1) == 1) | |
218 | *in_buf &= ~((uint64_t)0xff << i * 8); | |
219 | } | |
220 | for(int r_offset = 0; r_offset <= 4; r_offset++){// an extra iteration is needed to r/w the last byte | |
221 | if( (be >> r_offset & 0x1) == 1){ | |
222 | buf_1 |= (local_buf & 0xff) << r_offset * 8; | |
223 | to_rd_wr = true; | |
224 | bytes_to_rd_wr++; | |
225 | }else if(to_rd_wr){ | |
226 | ret |= confAccessSize(offset,wr, &buf_1,bytes_to_rd_wr); | |
227 | to_rd_wr = false; | |
228 | if(!wr) | |
229 | *in_buf |= buf_1 << (offset - orig_offset) * 8; | |
230 | offset += bytes_to_rd_wr; | |
231 | buf_1 = 0; | |
232 | bytes_to_rd_wr = 0; | |
233 | }else | |
234 | offset++; | |
235 | local_buf >>= 8; | |
236 | } | |
237 | return ret; | |
238 | } | |
239 | ||
240 | ||
241 | inline bool pciConfSpace::setMask(int offset, uint32_t mask){ | |
242 | pciConfIter = pciConfRegMap.lower_bound(offset); | |
243 | ||
244 | if(pciConfIter->first != offset || pciConfIter->second->isHole){ | |
245 | printf("conf space setMask: no register at offset 0x%x\n",offset); | |
246 | return false; | |
247 | } | |
248 | pciConfIter->second->setMask(mask); | |
249 | return true; | |
250 | } | |
251 | ||
252 | inline bool pciConfSpace::setVal(int offset, uint32_t value){ | |
253 | pciConfIter = pciConfRegMap.lower_bound(offset); | |
254 | ||
255 | if(pciConfIter->first != offset || pciConfIter->second->isHole){ | |
256 | printf("conf space setVal : no register at offset 0x%x\n",offset); | |
257 | return false; | |
258 | } | |
259 | pciConfIter->second->setVal(value); | |
260 | return true; | |
261 | } | |
262 | ||
263 | ||
264 | ||
265 | ||
266 | ||
267 |