summaryrefslogtreecommitdiffstats
path: root/qemu/roms/SLOF/board-qemu/slof/pci-phb.fs
blob: a8fb7ca5e233cda517e566fbed30eee6b62918a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
\ *****************************************************************************
\ * Copyright (c) 2004, 2011 IBM Corporation
\ * All rights reserved.
\ * This program and the accompanying materials
\ * are made available under the terms of the BSD License
\ * which accompanies this distribution, and is available at
\ * http://www.opensource.org/licenses/bsd-license.php
\ *
\ * Contributors:
\ *     IBM Corporation - initial implementation
\ ****************************************************************************/

\ PAPR PCI host bridge.

0 VALUE phb-debug?


." Populating " pwd cr

\ needed to find the right path in the device tree
: decode-unit ( addr len -- phys.lo ... phys.hi )
   2 hex-decode-unit       \ decode string
   b lshift swap           \ shift the devicenumber to the right spot
   8 lshift or             \ add the functionnumber
   \ my-bus 10 lshift or   \ add the busnumber (assume always bus 0)
   0 0 rot                 \ make phys.lo = 0 = phys.mid
;

\ needed to have the right unit address in the device tree listing
\ phys.lo=phys.mid=0 , phys.hi=config-address
: encode-unit ( phys.lo phys-mid phys.hi -- unit-str unit-len )
   nip nip                     \ forget the phys.lo and phys.mid
   dup 8 rshift 7 and swap     \ calculate function number
   B rshift 1F and             \ calculate device number
   over IF 2 ELSE nip 1 THEN   \ create string with dev#,fn# or dev# only?
   hex-encode-unit
;


0 VALUE my-puid

: setup-puid
  s" reg" get-node get-property 0= IF
    decode-64 to my-puid 2drop
  THEN
;

setup-puid

: config-b@  puid >r my-puid TO puid rtas-config-b@ r> TO puid ;
: config-w@  puid >r my-puid TO puid rtas-config-w@ r> TO puid ;
: config-l@  puid >r my-puid TO puid rtas-config-l@ r> TO puid ;

\ define the config writes
: config-b!  puid >r my-puid TO puid rtas-config-b! r> TO puid ;
: config-w!  puid >r my-puid TO puid rtas-config-w! r> TO puid ;
: config-l!  puid >r my-puid TO puid rtas-config-l! r> TO puid ;


: map-in ( phys.lo phys.mid phys.hi size -- virt )
   phb-debug? IF cr ." map-in called: " .s cr THEN
   \ Ignore the size, phys.lo and phys.mid, get BAR from config space
   drop nip nip                         ( phys.hi )
   \ Sanity check whether config address is in expected range:
   dup FF AND dup 10 28 WITHIN NOT swap 30 <> AND IF
      cr ." phys.hi = " . cr
      ABORT" map-in with illegal config space address"
   THEN
   00FFFFFF AND                         \ Need only bus-dev-fn+register bits
   dup config-l@                        ( phys.hi' bar.lo )
   dup 7 AND 4 = IF                     \ Is it a 64-bit BAR?
      swap 4 + config-l@ lxjoin         \ Add upper part of 64-bit BAR
   ELSE
      nip
   THEN
   F NOT AND                            \ Clear indicator bits
   translate-my-address
   phb-debug? IF ." map-in done: " .s cr THEN
;

: map-out ( virt size -- )
   phb-debug? IF ." map-out called: " .s cr THEN
   2drop 
;


: dma-alloc ( size -- virt )
   phb-debug? IF cr ." dma-alloc called: " .s cr THEN
   fff + fff not and                  \ Align size to next 4k boundary
   alloc-mem
   \ alloc-mem always returns aligned memory - double check just to be sure
   dup fff and IF
      ." Warning: dma-alloc got unaligned memory!" cr
   THEN
;

: dma-free ( virt size -- )
   phb-debug? IF cr ." dma-free called: " .s cr THEN
   fff + fff not and                  \ Align size to next 4k boundary
   free-mem
;


\ Helper variables for dma-map-in and dma-map-out
0 VALUE dma-window-liobn        \ Logical I/O bus number
0 VALUE dma-window-base         \ Start address of window
0 VALUE dma-window-size         \ Size of the window

0 VALUE bm-handle               \ Bitmap allocator handle
0 VALUE my-virt
0 VALUE my-size
0 VALUE dev-addr
0 VALUE tmp-dev-addr

\ Read helper variables (LIOBN, DMA window base and size) from the
\ "ibm,dma-window" property. This property can be either located
\ in the PCI device node or in the bus node, so we've got to use the
\ "calling-child" variable here to get to the node that initiated the call.
\ XXX We should search all the way up the tree to the PHB ...
: (init-dma-window-vars)  ( -- )
\   ." Foo called in " pwd cr
\   ." calling child is " calling-child .node cr
\   ." parent is " calling-child parent .node cr
   s" ibm,dma-window" calling-child get-property IF
       s" ibm,dma-window" calling-child parent get-property 
       ABORT" no dma-window property available"
   THEN
   decode-int TO dma-window-liobn
   decode-64 TO dma-window-base
   decode-64 TO dma-window-size
   2drop
   bm-handle 0= IF
       dma-window-base dma-window-size 1000 bm-allocator-init to bm-handle
       \ Sometimes the window-base appears as zero, that does not
       \ go well with NULL pointers. So block this address
       dma-window-base 0= IF
          bm-handle 1000 bm-alloc drop
       THEN
   THEN
;

: (clear-dma-window-vars)  ( -- )
    0 TO dma-window-liobn
    0 TO dma-window-base
    0 TO dma-window-size
;

\ We assume that firmware never maps more than the whole dma-window-size
\ so we cheat by calculating the remainder of addr/windowsize instead
\ of taking care to maintain a list of assigned device addresses
: dma-virt2dev  ( virt -- devaddr )
   dma-window-size mod dma-window-base +
;

: dma-map-in  ( virt size cachable? -- devaddr )
   phb-debug? IF cr ." dma-map-in called: " .s cr THEN
   (init-dma-window-vars)
   drop                               ( virt size )

   to my-size
   to my-virt
   bm-handle my-size bm-alloc
   to dev-addr
   dev-addr 0 < IF
       ." Bitmap allocation Failed " dev-addr .
       FALSE EXIT
   THEN
   dev-addr to tmp-dev-addr

   my-virt my-size
   bounds dup >r                      ( v+s virt  R: virt )
   swap fff + fff not and             \ Align end to next 4k boundary
   swap fff not and                   ( v+s' virt'  R: virt )
   ?DO
       \ ." mapping " i . cr
       dma-window-liobn                \ liobn
       tmp-dev-addr                    \ ioba
       i 3 OR                          \ Make a read- & writeable TCE
       ( liobn ioba tce  R: virt )
       hv-put-tce ABORT" H_PUT_TCE failed"
       tmp-dev-addr 1000 + to tmp-dev-addr
   1000 +LOOP
   r> drop
   my-virt FFF and dev-addr or
   (clear-dma-window-vars)
;

: dma-map-out  ( virt devaddr size -- )
   phb-debug? IF cr ." dma-map-out called: " .s cr THEN
   (init-dma-window-vars)
   to my-size
   to dev-addr
   to my-virt
   dev-addr fff not and to dev-addr
   dev-addr to tmp-dev-addr

   my-virt my-size                    ( virt size )
   bounds                             ( v+s virt )
   swap fff + fff not and             \ Align end to next 4k boundary
   swap fff not and                   ( v+s' virt' )
   ?DO
       \ ." unmapping " i . cr
       dma-window-liobn                \ liobn
       tmp-dev-addr                    \ ioba
       i                               \ Lowest bits not set => invalid TCE
       ( liobn ioba tce )
       hv-put-tce ABORT" H_PUT_TCE failed"
       tmp-dev-addr 1000 + to tmp-dev-addr
   1000 +LOOP
   bm-handle dev-addr my-size bm-free
   (clear-dma-window-vars)
;

: dma-sync  ( virt devaddr size -- )
   phb-debug? IF cr ." dma-sync called: " .s cr THEN
   \ TODO: Call flush-cache or sync here?
   3drop
;


: open  true ;
: close ;

\ Parse the "ranges" property of the root pci node to decode the available
\ memory ranges. See "PCI Bus Binding to IEEE Std 1275-1994" for details.
\ The memory ranges are then used for setting up the device bars (if necessary)
: phb-parse-ranges ( -- )
   \ First clear everything, in case there is something missing in the ranges
   0  pci-next-io !
   0  pci-max-io !
   0  pci-next-mem !
   0  pci-max-mem !
   0  pci-next-mmio !
   0  pci-max-mmio !
   0  pci-next-mem64 !
   0  pci-max-mem64 !

   \ Now get the "ranges" property
   s" ranges" get-node get-property 0<> ABORT" ranges property not found"
   ( prop-addr prop-len )
   BEGIN
      dup
   WHILE
      decode-int                      \ Decode phys.hi
      3000000 AND                     \ Filter out address space in phys.hi
      CASE
         1000000 OF                             \ I/O space?
            decode-64 dup >r pci-next-io !      \ Decode PCI base address
            decode-64 drop                      \ Forget the parent address
            decode-64 r> + pci-max-io !         \ Decode size & calc max address
            pci-next-io @ 0= IF
               pci-next-io @ 10 + pci-next-io ! \ BARs must not be set to zero
            THEN
         ENDOF
         2000000 OF                             \ 32-bit memory space?
            decode-64 pci-next-mem !            \ Decode mem base address
            decode-64 drop                      \ Forget the parent address
            decode-64 2 / dup >r                \ Decode and calc size/2
            pci-next-mem @ + dup pci-max-mem !  \ and calc max mem address
            dup pci-next-mmio !                 \ which is the same as MMIO base
            r> + pci-max-mmio !                 \ calc max MMIO address
         ENDOF
         3000000 OF                             \ 64-bit memory space?
	    decode-64 pci-next-mem64 !
	    decode-64 drop                      \ Forget the parent address
	    decode-64 pci-max-mem64 !
         ENDOF
      ENDCASE
   REPEAT
   ( prop-addr prop-len )
   2drop

   phb-debug? IF
     ." pci-next-io   = " pci-next-io @ . cr
     ." pci-max-io    = " pci-max-io  @ . cr
     ." pci-next-mem  = " pci-next-mem @ . cr
     ." pci-max-mem   = " pci-max-mem  @ . cr
     ." pci-next-mmio = " pci-next-mmio @ . cr
     ." pci-max-mmio  = " pci-max-mmio @ . cr
     ." pci-next-mem64  = " pci-next-mem64 @ . cr
     ." pci-max-mem64   = " pci-max-mem64  @ . cr
   THEN
;

: phb-pci-walk-bridge ( -- )
    phb-debug? IF ."   Calling pci-walk-bridge " pwd cr THEN

    get-node child ?dup 0= IF EXIT THEN    \ get and check if we have children
    0 to pci-device-slots                  \ reset slot array to unpoppulated
    BEGIN
        dup                                \ Continue as long as there are children
    WHILE
        dup set-node                       \ Set child node as current node
        my-space pci-set-slot              \ set the slot bit
        my-space pci-htype@                \ read HEADER-Type
        7f and                             \ Mask bit 7 - multifunction device
        CASE
            0 OF my-space pci-device-setup ENDOF  \ | set up the device
            1 OF my-space pci-bridge-setup ENDOF  \ | set up the bridge
            dup OF my-space pci-htype@ pci-out ENDOF
        ENDCASE
        peer
    REPEAT drop
    get-parent set-node
;

\ Landing routing to probe the popuated device tree
: phb-pci-probe-bus ( busnr -- )
    drop phb-pci-walk-bridge
;

\ Stub routine, as qemu has enumerated, we already have the device
\ properties set.
: phb-pci-device-props ( addr -- )
    dup pci-class-name device-name
    dup pci-device-assigned-addresses-prop
    drop
;

\ Scan the child nodes of the pci root node to assign bars, fixup
\ properties etc.
: phb-setup-children
   puid >r                          \ Save old value of puid
   my-puid TO puid                  \ Set current puid
   phb-parse-ranges
   1 TO pci-hotplug-enabled
   s" qemu,phb-enumerated" get-node get-property 0<> IF
       1 0 (probe-pci-host-bridge)
   ELSE
       2drop
       ['] phb-pci-probe-bus TO func-pci-probe-bus
       ['] phb-pci-device-props TO func-pci-device-props
       phb-pci-walk-bridge          \ PHB device tree is already populated.
   THEN
   r> TO puid                       \ Restore previous puid
;
phb-setup-children