summaryrefslogtreecommitdiffstats
path: root/src/usr/expscom/README.md
blob: 0710b49ce2a128ba5b978b59e562458af05fb084 (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
## Overall Description
The expscom module contains Hostboots device drivers to communicate with scom regs
on the Explorer Open-Capi Memory Buffer (OCMB) chip. Initially when Hostboot starts
the SCOM_SWITCHES attribute on all OCMB chips will be set such that useI2cScom field
is 1 and all other fields are 0. After OMI 's are trained the SCOM_SWITCHES attribute
on the OCMB targets will change so that the useIbScom field will be set to 1 and
all other fields will be 0.

### Explorer I2C SCOM

When the useI2cScom field is set in SCOM_SWITCHES this is how the
fapi2::getScom API for OCMB targets will be processed (lets say for now this is IBM scom address):

* Generic FAPI2 getScom API that will be called in a hardware procedure (HWP)


    fapi2::getScom(myOcmbTarget, scomAddr, io_buffer);
* Platform Specifc FAPI2 getScom API which the generic wrapper immediately calls


    fapi2::platGetScom(myOcmbTarget, scomAddr, io_buffer);
* Platform Specifc FAPI2 getScom API resolves to calling into Hostboot's device framework to whatever
function is registered to read OCMB target for DeviceFW::SCOM operations


    DeviceFW::deviceRead(myOcmbTarget, io_buffer,
                         sizeof(uint64_t), DeviceFW::SCOM,
                         scomAddr, READ)
* scomPeformOp is what is defined to handle DeviceFW::SCOM operations to the OCMB chip targets


    SCOM::scomPerformOp(READ, myOCMBTarget, io_buffer,
                        sizeof(uint64_t), DeviceFW::SCOM,
                        scomAddr)
* scomPeformOp is basically a wrapper for checkIndirectAndDoScom There are no indirect scoms for
OCMB target scoms so we will end up calling doScomOp


    checkIndirectAndDoScom(READ, myOCMBTarget, io_buffer,
                           sizeof(uint64_t), DeviceFW::SCOM,
                           scomAddr)

* doScomOp looks at the SCOM_SWITCHES attribute and decides which type of scom to do for the given target.


    doScomOp(READ, myOCMBTarget, io_buffer,
             sizeof(uint64_t), DeviceFW::SCOM,
             scomAddr)

* If the useI2cScom field is set to 1 then we will call the function that is registered to i2c scoms for OCMB targets


    deviceOp(READ, myOCMBTarget, io_buffer,
             sizeof(uint64_t), DeviceFW::I2CSCOM,
             scomAddr)

* i2cScomPerformOp is the function that is registered to handle i2cScom operations,
this function will perform some simple param validation before deciding whether to
call the getScom or putScom depending if the op is READ or WRITE respectively


    i2cScomPerformOp(READ, myOCMBTarget, io_buffer,
                     sizeof(uint64_t), DeviceFW::I2CSCOM,
                     scomAddr)

* The base device drivers are defined as EKB HWPs so we must run FAPI_EXEC_HWP in order to correctly call the i2c_get_scom hwp\
**Note: that EXEC and not INVOKE is used because it is likely this path
will be called within other HWPs and nesting INVOKE calls causes deadlock
due to the way we use mutexes.**


    FAPI_EXEC_HWP(l_rc , mss::exp::i2c::i2c_get_scom,
                  myOCMBTarget, static_cast<uint32_t>(l_scomAddr),
                  io_buffer);

* The Explorer chip nativly uses 32-bit regs so we must perform two 32-bit operations
(LHS and RHS) in order to get the 64-bits that we expect from the IBM scom


    fw_reg_read(myOCMBTarget,
                translateIbmI2cScomAddr(i_addr, LHS),
                l_readBuffer);

    fw_reg_read(myOCMBTarget,
                translateIbmI2cScomAddr(i_addr, RHS),
                l_readBuffer);

* The RHS and LHS fw_reg_read calls are nearly identical except the address is 4 bytes more
for the RHS. In both cases the way the fw_reg_read works is to first perform an i2cWrite stating
what address and how many bytes we want to read, then perform an i2c read to get the buffer


    < build up l_cmd_vector w/ FW_REG_ADDR_LATCH op, op size, addr >
    fapi2::putI2c(myOCMBTarget, FW_I2C_SCOM_READ_SIZE,
                  l_cmd_vector, l_read_data);

     < build up l_cmd_vector w/ FW_REG_READ op >
     fapi2::getI2c(myOCMBTarget, FW_I2C_SCOM_READ_SIZE,
                   l_cmd_vector, l_read_data);

* The fapi2::getI2c/putI2c calls will drill down into the drive routing similar to how we drilled down to find the i2cScom driver


    platGetI2c( myOCMBTarget, FW_I2C_SCOM_READ_SIZE
                l_cmd_vector, l_read_data)

* The hostboot platform specific implementation of platGetI2c will lookup the device route for FAPI_I2C ops to OCMB chips


    deviceRead( myOCMBTarget, l_read_data,
                FW_I2C_SCOM_READ_SIZE, DeviceFW::FAPI_I2C,
                sizeof(l_cmd_vector), l_cmd_vector);

* The function associated with FAPI_I2C ops to OCMB chips is fapiI2cPerformOp. This
wrapper function will look up i2c information about the OCMB target and determine
the i2c address for this operation


    fapiI2cPerformOp(READ , myOCMBTarget,
                     l_read_data, FW_I2C_SCOM_READ_SIZE,
                     DeviceFW::FAPI_I2C,  sizeof(l_cmd_vector),
                     l_cmd_vector);

* Eventually when the i2c info is known we call i2cRead/i2cWrite which will lookup
the device op route for I2C address on the OCMB's master I2c device (which will be a processor).


    i2cRead( myOCMBTargeti2cMaster, l_read_data,
             FW_I2C_SCOM_READ_SIZE, &l_myOCMBTargeti2cInfo,
             l_cmd_vector,  sizeof(l_cmd_vector));

* At this point we will drill down into the platform I2C device driver code


    deviceOp( DeviceFW::READ, myOCMBTargeti2cMaster,
              l_read_data, FW_I2C_SCOM_READ_SIZE,
              DEVICE_I2C_ADDRESS_OFFSET(l_myOCMBTargeti2cInfo->port,
                                        l_myOCMBTargeti2cInfo->engine,
                                        l_myOCMBTargeti2cInfo->devAddr,
                                        sizeof(l_cmd_vector),
                                        l_cmd_vector) );

### Explorer MMIO SCOM

When the useIbScom field is set in SCOM_SWITCHES this is how the fapi2::putScom API for OCMB
targets will be processed (lets say for now this is IBM scom address):

* Generic FAPI2 getScom API


    fapi2::getScom(myOcmbTarget, scomAddr, io_buffer);

* Platform Specific FAPI2 getScom API


    fapi2::platGetScom(myOcmbTarget, scomAddr, io_buffer);

* Platform Specific FAPI2 getScom API resolves to calling into our device framework to whatever
function is registered to read OCMB target for DeviceFW::SCOM operations


    DeviceFW::deviceRead(myOcmbTarget, io_buffer,
                         sizeof(uint64_t), DeviceFW::SCOM,
                         scomAddr, READ)

* scomPeformOp is what is defined to handle DeviceFW::SCOM operations to the OCMB chip targets


    SCOM::scomPerformOp(READ, myOCMBTarget, io_buffer,
                        sizeof(uint64_t), DeviceFW::SCOM,
                        scomAddr)

* scomPeformOp is basically a wrapper for checkIndirectAndDoScom. There are no indirect scoms
for OCMB target scoms so we will end up calling doScomOp


    checkIndirectAndDoScom(READ, myOCMBTarget, io_buffer,
                           sizeof(uint64_t), DeviceFW::SCOM,
                           scomAddr)

* doScomOp looks at the SCOM_SWITCHES attribute and decides which type of scom to do for the given target.


    doScomOp(READ, myOCMBTarget, io_buffer,
             sizeof(uint64_t), DeviceFW::SCOM,
             scomAddr)

* If the useIbScom field is set to 1 then we will call the function that is registered to inband scoms for OCMB targets


    deviceOp(READ, myOCMBTarget, io_buffer,
             sizeof(uint64_t), DeviceFW::IBSCOM,
             scomAddr)

* mmioScomPerformOp is the function that is registered to IBSCOM operations to OCMB chips


    mmioScomPerformOp(READ, myOCMBTarget, io_buffer,
                      sizeof(uint64_t), DeviceFW::IBSCOM,
                      scomAddr)

* mmioScomPerformOp will call the hwp mss::exp::ib::getScom which is a in-band scom driver for the OCMB explorer chip


    FAPI_EXEC_HWP(l_rc , mss::exp::ib::getScom, myOCMBTarget, scomAddr, io_buffer);

* mss::exp::ib::getScom will translate the scomAddress into a mmio address and perform a getMMIO64 operation


    getMMIO64(myOCMBTarget, (scomAddr << 3), io_buffer);

* getMMIO64 will add the IB_MMIO offset and perform a 64 bit mmio read using the fapi2::getMMIO interface


    fapi2::getMMIO(myOCMBTarget, EXPLR_IB_MMIO_OFFSET | scomAddr, 8, io_buffer)

* fapi2::getMMIO is defined by the platform


    ReturnCode platGetMMIO( myOCMBTarget,
                            EXPLR_IB_MMIO_OFFSET | (scomAddr << 3),
                            8,  // bytes
                            io_buffer )

* platGetMMIO will use the device framework to look up the correct routine for MMIO addresses on OCMB targets


    DeviceFW::deviceRead(myOCMBTarget,
                         io_buffer,
                         8,  // bytes
                         DEVICE_MMIO_ADDRESS(EXPLR_IB_MMIO_OFFSET | (scomAddr << 3), 8));


* the device framework will route the deviceRead call to mmioPerformOp


    mmioPerformOp(READ,
                  myOCMBTarget,
                  io_buffer,
                  8, // bytes
                  DeviceFW::MMIO,
                  address, readLimit);

* mmioPerformOp will execute the actual load/store operation input memory-mapped i/o
space and handle errors in that operation


    if (i_opType == DeviceFW::READ)
    {
        memcpy(io_ptr + i, mm_ptr + i, l_accessLimit);
    }
    else if (i_opType == DeviceFW::WRITE)
    {
        memcpy(mm_ptr + i, io_ptr + i, l_accessLimit);

        // XXX Need to check a processor SCOM here to determine if the
        // write succeeded or failed.
    }
OpenPOWER on IntegriCloud