xref: /qemu/hw/pci/pcie_doe.c (revision 5fb52f6c)
1*5fb52f6cSHuai-Cheng Kuo /*
2*5fb52f6cSHuai-Cheng Kuo  * PCIe Data Object Exchange
3*5fb52f6cSHuai-Cheng Kuo  *
4*5fb52f6cSHuai-Cheng Kuo  * Copyright (C) 2021 Avery Design Systems, Inc.
5*5fb52f6cSHuai-Cheng Kuo  *
6*5fb52f6cSHuai-Cheng Kuo  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7*5fb52f6cSHuai-Cheng Kuo  * See the COPYING file in the top-level directory.
8*5fb52f6cSHuai-Cheng Kuo  */
9*5fb52f6cSHuai-Cheng Kuo 
10*5fb52f6cSHuai-Cheng Kuo #include "qemu/osdep.h"
11*5fb52f6cSHuai-Cheng Kuo #include "qemu/log.h"
12*5fb52f6cSHuai-Cheng Kuo #include "qemu/error-report.h"
13*5fb52f6cSHuai-Cheng Kuo #include "qapi/error.h"
14*5fb52f6cSHuai-Cheng Kuo #include "qemu/range.h"
15*5fb52f6cSHuai-Cheng Kuo #include "hw/pci/pci.h"
16*5fb52f6cSHuai-Cheng Kuo #include "hw/pci/pcie.h"
17*5fb52f6cSHuai-Cheng Kuo #include "hw/pci/pcie_doe.h"
18*5fb52f6cSHuai-Cheng Kuo #include "hw/pci/msi.h"
19*5fb52f6cSHuai-Cheng Kuo #include "hw/pci/msix.h"
20*5fb52f6cSHuai-Cheng Kuo 
21*5fb52f6cSHuai-Cheng Kuo #define DWORD_BYTE 4
22*5fb52f6cSHuai-Cheng Kuo 
23*5fb52f6cSHuai-Cheng Kuo typedef struct DoeDiscoveryReq {
24*5fb52f6cSHuai-Cheng Kuo     DOEHeader header;
25*5fb52f6cSHuai-Cheng Kuo     uint8_t index;
26*5fb52f6cSHuai-Cheng Kuo     uint8_t reserved[3];
27*5fb52f6cSHuai-Cheng Kuo } QEMU_PACKED DoeDiscoveryReq;
28*5fb52f6cSHuai-Cheng Kuo 
29*5fb52f6cSHuai-Cheng Kuo typedef struct DoeDiscoveryRsp {
30*5fb52f6cSHuai-Cheng Kuo     DOEHeader header;
31*5fb52f6cSHuai-Cheng Kuo     uint16_t vendor_id;
32*5fb52f6cSHuai-Cheng Kuo     uint8_t data_obj_type;
33*5fb52f6cSHuai-Cheng Kuo     uint8_t next_index;
34*5fb52f6cSHuai-Cheng Kuo } QEMU_PACKED DoeDiscoveryRsp;
35*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_discovery(DOECap * doe_cap)36*5fb52f6cSHuai-Cheng Kuo static bool pcie_doe_discovery(DOECap *doe_cap)
37*5fb52f6cSHuai-Cheng Kuo {
38*5fb52f6cSHuai-Cheng Kuo     DoeDiscoveryReq *req = pcie_doe_get_write_mbox_ptr(doe_cap);
39*5fb52f6cSHuai-Cheng Kuo     DoeDiscoveryRsp rsp;
40*5fb52f6cSHuai-Cheng Kuo     uint8_t index = req->index;
41*5fb52f6cSHuai-Cheng Kuo     DOEProtocol *prot;
42*5fb52f6cSHuai-Cheng Kuo 
43*5fb52f6cSHuai-Cheng Kuo     /* Discard request if length does not match DoeDiscoveryReq */
44*5fb52f6cSHuai-Cheng Kuo     if (pcie_doe_get_obj_len(req) <
45*5fb52f6cSHuai-Cheng Kuo         DIV_ROUND_UP(sizeof(DoeDiscoveryReq), DWORD_BYTE)) {
46*5fb52f6cSHuai-Cheng Kuo         return false;
47*5fb52f6cSHuai-Cheng Kuo     }
48*5fb52f6cSHuai-Cheng Kuo 
49*5fb52f6cSHuai-Cheng Kuo     rsp.header = (DOEHeader) {
50*5fb52f6cSHuai-Cheng Kuo         .vendor_id = PCI_VENDOR_ID_PCI_SIG,
51*5fb52f6cSHuai-Cheng Kuo         .data_obj_type = PCI_SIG_DOE_DISCOVERY,
52*5fb52f6cSHuai-Cheng Kuo         .length = DIV_ROUND_UP(sizeof(DoeDiscoveryRsp), DWORD_BYTE),
53*5fb52f6cSHuai-Cheng Kuo     };
54*5fb52f6cSHuai-Cheng Kuo 
55*5fb52f6cSHuai-Cheng Kuo     /* Point to the requested protocol, index 0 must be Discovery */
56*5fb52f6cSHuai-Cheng Kuo     if (index == 0) {
57*5fb52f6cSHuai-Cheng Kuo         rsp.vendor_id = PCI_VENDOR_ID_PCI_SIG;
58*5fb52f6cSHuai-Cheng Kuo         rsp.data_obj_type = PCI_SIG_DOE_DISCOVERY;
59*5fb52f6cSHuai-Cheng Kuo     } else {
60*5fb52f6cSHuai-Cheng Kuo         if (index < doe_cap->protocol_num) {
61*5fb52f6cSHuai-Cheng Kuo             prot = &doe_cap->protocols[index - 1];
62*5fb52f6cSHuai-Cheng Kuo             rsp.vendor_id = prot->vendor_id;
63*5fb52f6cSHuai-Cheng Kuo             rsp.data_obj_type = prot->data_obj_type;
64*5fb52f6cSHuai-Cheng Kuo         } else {
65*5fb52f6cSHuai-Cheng Kuo             rsp.vendor_id = 0xFFFF;
66*5fb52f6cSHuai-Cheng Kuo             rsp.data_obj_type = 0xFF;
67*5fb52f6cSHuai-Cheng Kuo         }
68*5fb52f6cSHuai-Cheng Kuo     }
69*5fb52f6cSHuai-Cheng Kuo 
70*5fb52f6cSHuai-Cheng Kuo     if (index + 1 == doe_cap->protocol_num) {
71*5fb52f6cSHuai-Cheng Kuo         rsp.next_index = 0;
72*5fb52f6cSHuai-Cheng Kuo     } else {
73*5fb52f6cSHuai-Cheng Kuo         rsp.next_index = index + 1;
74*5fb52f6cSHuai-Cheng Kuo     }
75*5fb52f6cSHuai-Cheng Kuo 
76*5fb52f6cSHuai-Cheng Kuo     pcie_doe_set_rsp(doe_cap, &rsp);
77*5fb52f6cSHuai-Cheng Kuo 
78*5fb52f6cSHuai-Cheng Kuo     return true;
79*5fb52f6cSHuai-Cheng Kuo }
80*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_reset_mbox(DOECap * st)81*5fb52f6cSHuai-Cheng Kuo static void pcie_doe_reset_mbox(DOECap *st)
82*5fb52f6cSHuai-Cheng Kuo {
83*5fb52f6cSHuai-Cheng Kuo     st->read_mbox_idx = 0;
84*5fb52f6cSHuai-Cheng Kuo     st->read_mbox_len = 0;
85*5fb52f6cSHuai-Cheng Kuo     st->write_mbox_len = 0;
86*5fb52f6cSHuai-Cheng Kuo 
87*5fb52f6cSHuai-Cheng Kuo     memset(st->read_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
88*5fb52f6cSHuai-Cheng Kuo     memset(st->write_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
89*5fb52f6cSHuai-Cheng Kuo }
90*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_init(PCIDevice * dev,DOECap * doe_cap,uint16_t offset,DOEProtocol * protocols,bool intr,uint16_t vec)91*5fb52f6cSHuai-Cheng Kuo void pcie_doe_init(PCIDevice *dev, DOECap *doe_cap, uint16_t offset,
92*5fb52f6cSHuai-Cheng Kuo                    DOEProtocol *protocols, bool intr, uint16_t vec)
93*5fb52f6cSHuai-Cheng Kuo {
94*5fb52f6cSHuai-Cheng Kuo     pcie_add_capability(dev, PCI_EXT_CAP_ID_DOE, 0x1, offset,
95*5fb52f6cSHuai-Cheng Kuo                         PCI_DOE_SIZEOF);
96*5fb52f6cSHuai-Cheng Kuo 
97*5fb52f6cSHuai-Cheng Kuo     doe_cap->pdev = dev;
98*5fb52f6cSHuai-Cheng Kuo     doe_cap->offset = offset;
99*5fb52f6cSHuai-Cheng Kuo 
100*5fb52f6cSHuai-Cheng Kuo     if (intr && (msi_present(dev) || msix_present(dev))) {
101*5fb52f6cSHuai-Cheng Kuo         doe_cap->cap.intr = intr;
102*5fb52f6cSHuai-Cheng Kuo         doe_cap->cap.vec = vec;
103*5fb52f6cSHuai-Cheng Kuo     }
104*5fb52f6cSHuai-Cheng Kuo 
105*5fb52f6cSHuai-Cheng Kuo     doe_cap->write_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
106*5fb52f6cSHuai-Cheng Kuo     doe_cap->read_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
107*5fb52f6cSHuai-Cheng Kuo 
108*5fb52f6cSHuai-Cheng Kuo     pcie_doe_reset_mbox(doe_cap);
109*5fb52f6cSHuai-Cheng Kuo 
110*5fb52f6cSHuai-Cheng Kuo     doe_cap->protocols = protocols;
111*5fb52f6cSHuai-Cheng Kuo     for (; protocols->vendor_id; protocols++) {
112*5fb52f6cSHuai-Cheng Kuo         doe_cap->protocol_num++;
113*5fb52f6cSHuai-Cheng Kuo     }
114*5fb52f6cSHuai-Cheng Kuo     assert(doe_cap->protocol_num < PCI_DOE_PROTOCOL_NUM_MAX);
115*5fb52f6cSHuai-Cheng Kuo 
116*5fb52f6cSHuai-Cheng Kuo     /* Increment to allow for the discovery protocol */
117*5fb52f6cSHuai-Cheng Kuo     doe_cap->protocol_num++;
118*5fb52f6cSHuai-Cheng Kuo }
119*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_fini(DOECap * doe_cap)120*5fb52f6cSHuai-Cheng Kuo void pcie_doe_fini(DOECap *doe_cap)
121*5fb52f6cSHuai-Cheng Kuo {
122*5fb52f6cSHuai-Cheng Kuo     g_free(doe_cap->read_mbox);
123*5fb52f6cSHuai-Cheng Kuo     g_free(doe_cap->write_mbox);
124*5fb52f6cSHuai-Cheng Kuo     g_free(doe_cap);
125*5fb52f6cSHuai-Cheng Kuo }
126*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_build_protocol(DOEProtocol * p)127*5fb52f6cSHuai-Cheng Kuo uint32_t pcie_doe_build_protocol(DOEProtocol *p)
128*5fb52f6cSHuai-Cheng Kuo {
129*5fb52f6cSHuai-Cheng Kuo     return DATA_OBJ_BUILD_HEADER1(p->vendor_id, p->data_obj_type);
130*5fb52f6cSHuai-Cheng Kuo }
131*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_get_write_mbox_ptr(DOECap * doe_cap)132*5fb52f6cSHuai-Cheng Kuo void *pcie_doe_get_write_mbox_ptr(DOECap *doe_cap)
133*5fb52f6cSHuai-Cheng Kuo {
134*5fb52f6cSHuai-Cheng Kuo     return doe_cap->write_mbox;
135*5fb52f6cSHuai-Cheng Kuo }
136*5fb52f6cSHuai-Cheng Kuo 
137*5fb52f6cSHuai-Cheng Kuo /*
138*5fb52f6cSHuai-Cheng Kuo  * Copy the response to read mailbox buffer
139*5fb52f6cSHuai-Cheng Kuo  * This might be called in self-defined handle_request() if a DOE response is
140*5fb52f6cSHuai-Cheng Kuo  * required in the corresponding protocol
141*5fb52f6cSHuai-Cheng Kuo  */
pcie_doe_set_rsp(DOECap * doe_cap,void * rsp)142*5fb52f6cSHuai-Cheng Kuo void pcie_doe_set_rsp(DOECap *doe_cap, void *rsp)
143*5fb52f6cSHuai-Cheng Kuo {
144*5fb52f6cSHuai-Cheng Kuo     uint32_t len = pcie_doe_get_obj_len(rsp);
145*5fb52f6cSHuai-Cheng Kuo 
146*5fb52f6cSHuai-Cheng Kuo     memcpy(doe_cap->read_mbox + doe_cap->read_mbox_len, rsp, len * DWORD_BYTE);
147*5fb52f6cSHuai-Cheng Kuo     doe_cap->read_mbox_len += len;
148*5fb52f6cSHuai-Cheng Kuo }
149*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_get_obj_len(void * obj)150*5fb52f6cSHuai-Cheng Kuo uint32_t pcie_doe_get_obj_len(void *obj)
151*5fb52f6cSHuai-Cheng Kuo {
152*5fb52f6cSHuai-Cheng Kuo     uint32_t len;
153*5fb52f6cSHuai-Cheng Kuo 
154*5fb52f6cSHuai-Cheng Kuo     if (!obj) {
155*5fb52f6cSHuai-Cheng Kuo         return 0;
156*5fb52f6cSHuai-Cheng Kuo     }
157*5fb52f6cSHuai-Cheng Kuo 
158*5fb52f6cSHuai-Cheng Kuo     /* Only lower 18 bits are valid */
159*5fb52f6cSHuai-Cheng Kuo     len = DATA_OBJ_LEN_MASK(((DOEHeader *)obj)->length);
160*5fb52f6cSHuai-Cheng Kuo 
161*5fb52f6cSHuai-Cheng Kuo     /* PCIe r6.0 Table 6.29: a value of 00000h indicates 2^18 DW */
162*5fb52f6cSHuai-Cheng Kuo     return (len) ? len : PCI_DOE_DW_SIZE_MAX;
163*5fb52f6cSHuai-Cheng Kuo }
164*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_irq_assert(DOECap * doe_cap)165*5fb52f6cSHuai-Cheng Kuo static void pcie_doe_irq_assert(DOECap *doe_cap)
166*5fb52f6cSHuai-Cheng Kuo {
167*5fb52f6cSHuai-Cheng Kuo     PCIDevice *dev = doe_cap->pdev;
168*5fb52f6cSHuai-Cheng Kuo 
169*5fb52f6cSHuai-Cheng Kuo     if (doe_cap->cap.intr && doe_cap->ctrl.intr) {
170*5fb52f6cSHuai-Cheng Kuo         if (doe_cap->status.intr) {
171*5fb52f6cSHuai-Cheng Kuo             return;
172*5fb52f6cSHuai-Cheng Kuo         }
173*5fb52f6cSHuai-Cheng Kuo         doe_cap->status.intr = 1;
174*5fb52f6cSHuai-Cheng Kuo 
175*5fb52f6cSHuai-Cheng Kuo         if (msix_enabled(dev)) {
176*5fb52f6cSHuai-Cheng Kuo             msix_notify(dev, doe_cap->cap.vec);
177*5fb52f6cSHuai-Cheng Kuo         } else if (msi_enabled(dev)) {
178*5fb52f6cSHuai-Cheng Kuo             msi_notify(dev, doe_cap->cap.vec);
179*5fb52f6cSHuai-Cheng Kuo         }
180*5fb52f6cSHuai-Cheng Kuo     }
181*5fb52f6cSHuai-Cheng Kuo }
182*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_set_ready(DOECap * doe_cap,bool rdy)183*5fb52f6cSHuai-Cheng Kuo static void pcie_doe_set_ready(DOECap *doe_cap, bool rdy)
184*5fb52f6cSHuai-Cheng Kuo {
185*5fb52f6cSHuai-Cheng Kuo     doe_cap->status.ready = rdy;
186*5fb52f6cSHuai-Cheng Kuo 
187*5fb52f6cSHuai-Cheng Kuo     if (rdy) {
188*5fb52f6cSHuai-Cheng Kuo         pcie_doe_irq_assert(doe_cap);
189*5fb52f6cSHuai-Cheng Kuo     }
190*5fb52f6cSHuai-Cheng Kuo }
191*5fb52f6cSHuai-Cheng Kuo 
pcie_doe_set_error(DOECap * doe_cap,bool err)192*5fb52f6cSHuai-Cheng Kuo static void pcie_doe_set_error(DOECap *doe_cap, bool err)
193*5fb52f6cSHuai-Cheng Kuo {
194*5fb52f6cSHuai-Cheng Kuo     doe_cap->status.error = err;
195*5fb52f6cSHuai-Cheng Kuo 
196*5fb52f6cSHuai-Cheng Kuo     if (err) {
197*5fb52f6cSHuai-Cheng Kuo         pcie_doe_irq_assert(doe_cap);
198*5fb52f6cSHuai-Cheng Kuo     }
199*5fb52f6cSHuai-Cheng Kuo }
200*5fb52f6cSHuai-Cheng Kuo 
201*5fb52f6cSHuai-Cheng Kuo /*
202*5fb52f6cSHuai-Cheng Kuo  * Check incoming request in write_mbox for protocol format
203*5fb52f6cSHuai-Cheng Kuo  */
pcie_doe_prepare_rsp(DOECap * doe_cap)204*5fb52f6cSHuai-Cheng Kuo static void pcie_doe_prepare_rsp(DOECap *doe_cap)
205*5fb52f6cSHuai-Cheng Kuo {
206*5fb52f6cSHuai-Cheng Kuo     bool success = false;
207*5fb52f6cSHuai-Cheng Kuo     int p;
208*5fb52f6cSHuai-Cheng Kuo     bool (*handle_request)(DOECap *) = NULL;
209*5fb52f6cSHuai-Cheng Kuo 
210*5fb52f6cSHuai-Cheng Kuo     if (doe_cap->status.error) {
211*5fb52f6cSHuai-Cheng Kuo         return;
212*5fb52f6cSHuai-Cheng Kuo     }
213*5fb52f6cSHuai-Cheng Kuo 
214*5fb52f6cSHuai-Cheng Kuo     if (doe_cap->write_mbox[0] ==
215*5fb52f6cSHuai-Cheng Kuo         DATA_OBJ_BUILD_HEADER1(PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_DISCOVERY)) {
216*5fb52f6cSHuai-Cheng Kuo         handle_request = pcie_doe_discovery;
217*5fb52f6cSHuai-Cheng Kuo     } else {
218*5fb52f6cSHuai-Cheng Kuo         for (p = 0; p < doe_cap->protocol_num - 1; p++) {
219*5fb52f6cSHuai-Cheng Kuo             if (doe_cap->write_mbox[0] ==
220*5fb52f6cSHuai-Cheng Kuo                 pcie_doe_build_protocol(&doe_cap->protocols[p])) {
221*5fb52f6cSHuai-Cheng Kuo                 handle_request = doe_cap->protocols[p].handle_request;
222*5fb52f6cSHuai-Cheng Kuo                 break;
223*5fb52f6cSHuai-Cheng Kuo             }
224*5fb52f6cSHuai-Cheng Kuo         }
225*5fb52f6cSHuai-Cheng Kuo     }
226*5fb52f6cSHuai-Cheng Kuo 
227*5fb52f6cSHuai-Cheng Kuo     /*
228*5fb52f6cSHuai-Cheng Kuo      * PCIe r6 DOE 6.30.1:
229*5fb52f6cSHuai-Cheng Kuo      * If the number of DW transferred does not match the
230*5fb52f6cSHuai-Cheng Kuo      * indicated Length for a data object, then the
231*5fb52f6cSHuai-Cheng Kuo      * data object must be silently discarded.
232*5fb52f6cSHuai-Cheng Kuo      */
233*5fb52f6cSHuai-Cheng Kuo     if (handle_request && (doe_cap->write_mbox_len ==
234*5fb52f6cSHuai-Cheng Kuo         pcie_doe_get_obj_len(pcie_doe_get_write_mbox_ptr(doe_cap)))) {
235*5fb52f6cSHuai-Cheng Kuo         success = handle_request(doe_cap);
236*5fb52f6cSHuai-Cheng Kuo     }
237*5fb52f6cSHuai-Cheng Kuo 
238*5fb52f6cSHuai-Cheng Kuo     if (success) {
239*5fb52f6cSHuai-Cheng Kuo         pcie_doe_set_ready(doe_cap, 1);
240*5fb52f6cSHuai-Cheng Kuo     } else {
241*5fb52f6cSHuai-Cheng Kuo         pcie_doe_reset_mbox(doe_cap);
242*5fb52f6cSHuai-Cheng Kuo     }
243*5fb52f6cSHuai-Cheng Kuo }
244*5fb52f6cSHuai-Cheng Kuo 
245*5fb52f6cSHuai-Cheng Kuo /*
246*5fb52f6cSHuai-Cheng Kuo  * Read from DOE config space.
247*5fb52f6cSHuai-Cheng Kuo  * Return false if the address not within DOE_CAP range.
248*5fb52f6cSHuai-Cheng Kuo  */
pcie_doe_read_config(DOECap * doe_cap,uint32_t addr,int size,uint32_t * buf)249*5fb52f6cSHuai-Cheng Kuo bool pcie_doe_read_config(DOECap *doe_cap, uint32_t addr, int size,
250*5fb52f6cSHuai-Cheng Kuo                           uint32_t *buf)
251*5fb52f6cSHuai-Cheng Kuo {
252*5fb52f6cSHuai-Cheng Kuo     uint32_t shift;
253*5fb52f6cSHuai-Cheng Kuo     uint16_t doe_offset = doe_cap->offset;
254*5fb52f6cSHuai-Cheng Kuo 
255*5fb52f6cSHuai-Cheng Kuo     if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP,
256*5fb52f6cSHuai-Cheng Kuo                            PCI_DOE_SIZEOF - 4, addr)) {
257*5fb52f6cSHuai-Cheng Kuo         return false;
258*5fb52f6cSHuai-Cheng Kuo     }
259*5fb52f6cSHuai-Cheng Kuo 
260*5fb52f6cSHuai-Cheng Kuo     addr -= doe_offset;
261*5fb52f6cSHuai-Cheng Kuo     *buf = 0;
262*5fb52f6cSHuai-Cheng Kuo 
263*5fb52f6cSHuai-Cheng Kuo     if (range_covers_byte(PCI_EXP_DOE_CAP, DWORD_BYTE, addr)) {
264*5fb52f6cSHuai-Cheng Kuo         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, INTR_SUPP,
265*5fb52f6cSHuai-Cheng Kuo                           doe_cap->cap.intr);
266*5fb52f6cSHuai-Cheng Kuo         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, DOE_INTR_MSG_NUM,
267*5fb52f6cSHuai-Cheng Kuo                           doe_cap->cap.vec);
268*5fb52f6cSHuai-Cheng Kuo     } else if (range_covers_byte(PCI_EXP_DOE_CTRL, DWORD_BYTE, addr)) {
269*5fb52f6cSHuai-Cheng Kuo         /* Must return ABORT=0 and GO=0 */
270*5fb52f6cSHuai-Cheng Kuo         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_CONTROL, DOE_INTR_EN,
271*5fb52f6cSHuai-Cheng Kuo                           doe_cap->ctrl.intr);
272*5fb52f6cSHuai-Cheng Kuo     } else if (range_covers_byte(PCI_EXP_DOE_STATUS, DWORD_BYTE, addr)) {
273*5fb52f6cSHuai-Cheng Kuo         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_BUSY,
274*5fb52f6cSHuai-Cheng Kuo                           doe_cap->status.busy);
275*5fb52f6cSHuai-Cheng Kuo         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS,
276*5fb52f6cSHuai-Cheng Kuo                           doe_cap->status.intr);
277*5fb52f6cSHuai-Cheng Kuo         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_ERROR,
278*5fb52f6cSHuai-Cheng Kuo                           doe_cap->status.error);
279*5fb52f6cSHuai-Cheng Kuo         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DATA_OBJ_RDY,
280*5fb52f6cSHuai-Cheng Kuo                           doe_cap->status.ready);
281*5fb52f6cSHuai-Cheng Kuo     /* Mailbox should be DW accessed */
282*5fb52f6cSHuai-Cheng Kuo     } else if (addr == PCI_EXP_DOE_RD_DATA_MBOX && size == DWORD_BYTE) {
283*5fb52f6cSHuai-Cheng Kuo         if (doe_cap->status.ready && !doe_cap->status.error) {
284*5fb52f6cSHuai-Cheng Kuo             *buf = doe_cap->read_mbox[doe_cap->read_mbox_idx];
285*5fb52f6cSHuai-Cheng Kuo         }
286*5fb52f6cSHuai-Cheng Kuo     }
287*5fb52f6cSHuai-Cheng Kuo 
288*5fb52f6cSHuai-Cheng Kuo     /* Process Alignment */
289*5fb52f6cSHuai-Cheng Kuo     shift = addr % DWORD_BYTE;
290*5fb52f6cSHuai-Cheng Kuo     *buf = extract32(*buf, shift * 8, size * 8);
291*5fb52f6cSHuai-Cheng Kuo 
292*5fb52f6cSHuai-Cheng Kuo     return true;
293*5fb52f6cSHuai-Cheng Kuo }
294*5fb52f6cSHuai-Cheng Kuo 
295*5fb52f6cSHuai-Cheng Kuo /*
296*5fb52f6cSHuai-Cheng Kuo  * Write to DOE config space.
297*5fb52f6cSHuai-Cheng Kuo  * Return if the address not within DOE_CAP range or receives an abort
298*5fb52f6cSHuai-Cheng Kuo  */
pcie_doe_write_config(DOECap * doe_cap,uint32_t addr,uint32_t val,int size)299*5fb52f6cSHuai-Cheng Kuo void pcie_doe_write_config(DOECap *doe_cap,
300*5fb52f6cSHuai-Cheng Kuo                            uint32_t addr, uint32_t val, int size)
301*5fb52f6cSHuai-Cheng Kuo {
302*5fb52f6cSHuai-Cheng Kuo     uint16_t doe_offset = doe_cap->offset;
303*5fb52f6cSHuai-Cheng Kuo     uint32_t shift;
304*5fb52f6cSHuai-Cheng Kuo 
305*5fb52f6cSHuai-Cheng Kuo     if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP,
306*5fb52f6cSHuai-Cheng Kuo                            PCI_DOE_SIZEOF - 4, addr)) {
307*5fb52f6cSHuai-Cheng Kuo         return;
308*5fb52f6cSHuai-Cheng Kuo     }
309*5fb52f6cSHuai-Cheng Kuo 
310*5fb52f6cSHuai-Cheng Kuo     /* Process Alignment */
311*5fb52f6cSHuai-Cheng Kuo     shift = addr % DWORD_BYTE;
312*5fb52f6cSHuai-Cheng Kuo     addr -= (doe_offset + shift);
313*5fb52f6cSHuai-Cheng Kuo     val = deposit32(val, shift * 8, size * 8, val);
314*5fb52f6cSHuai-Cheng Kuo 
315*5fb52f6cSHuai-Cheng Kuo     switch (addr) {
316*5fb52f6cSHuai-Cheng Kuo     case PCI_EXP_DOE_CTRL:
317*5fb52f6cSHuai-Cheng Kuo         if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_ABORT)) {
318*5fb52f6cSHuai-Cheng Kuo             pcie_doe_set_ready(doe_cap, 0);
319*5fb52f6cSHuai-Cheng Kuo             pcie_doe_set_error(doe_cap, 0);
320*5fb52f6cSHuai-Cheng Kuo             pcie_doe_reset_mbox(doe_cap);
321*5fb52f6cSHuai-Cheng Kuo             return;
322*5fb52f6cSHuai-Cheng Kuo         }
323*5fb52f6cSHuai-Cheng Kuo 
324*5fb52f6cSHuai-Cheng Kuo         if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_GO)) {
325*5fb52f6cSHuai-Cheng Kuo             pcie_doe_prepare_rsp(doe_cap);
326*5fb52f6cSHuai-Cheng Kuo         }
327*5fb52f6cSHuai-Cheng Kuo 
328*5fb52f6cSHuai-Cheng Kuo         if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_INTR_EN)) {
329*5fb52f6cSHuai-Cheng Kuo             doe_cap->ctrl.intr = 1;
330*5fb52f6cSHuai-Cheng Kuo         /* Clear interrupt bit located within the first byte */
331*5fb52f6cSHuai-Cheng Kuo         } else if (shift == 0) {
332*5fb52f6cSHuai-Cheng Kuo             doe_cap->ctrl.intr = 0;
333*5fb52f6cSHuai-Cheng Kuo         }
334*5fb52f6cSHuai-Cheng Kuo         break;
335*5fb52f6cSHuai-Cheng Kuo     case PCI_EXP_DOE_STATUS:
336*5fb52f6cSHuai-Cheng Kuo         if (FIELD_EX32(val, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS)) {
337*5fb52f6cSHuai-Cheng Kuo             doe_cap->status.intr = 0;
338*5fb52f6cSHuai-Cheng Kuo         }
339*5fb52f6cSHuai-Cheng Kuo         break;
340*5fb52f6cSHuai-Cheng Kuo     case PCI_EXP_DOE_RD_DATA_MBOX:
341*5fb52f6cSHuai-Cheng Kuo         /* Mailbox should be DW accessed */
342*5fb52f6cSHuai-Cheng Kuo         if (size != DWORD_BYTE) {
343*5fb52f6cSHuai-Cheng Kuo             return;
344*5fb52f6cSHuai-Cheng Kuo         }
345*5fb52f6cSHuai-Cheng Kuo         doe_cap->read_mbox_idx++;
346*5fb52f6cSHuai-Cheng Kuo         if (doe_cap->read_mbox_idx == doe_cap->read_mbox_len) {
347*5fb52f6cSHuai-Cheng Kuo             pcie_doe_reset_mbox(doe_cap);
348*5fb52f6cSHuai-Cheng Kuo             pcie_doe_set_ready(doe_cap, 0);
349*5fb52f6cSHuai-Cheng Kuo         } else if (doe_cap->read_mbox_idx > doe_cap->read_mbox_len) {
350*5fb52f6cSHuai-Cheng Kuo             /* Underflow */
351*5fb52f6cSHuai-Cheng Kuo             pcie_doe_set_error(doe_cap, 1);
352*5fb52f6cSHuai-Cheng Kuo         }
353*5fb52f6cSHuai-Cheng Kuo         break;
354*5fb52f6cSHuai-Cheng Kuo     case PCI_EXP_DOE_WR_DATA_MBOX:
355*5fb52f6cSHuai-Cheng Kuo         /* Mailbox should be DW accessed */
356*5fb52f6cSHuai-Cheng Kuo         if (size != DWORD_BYTE) {
357*5fb52f6cSHuai-Cheng Kuo             return;
358*5fb52f6cSHuai-Cheng Kuo         }
359*5fb52f6cSHuai-Cheng Kuo         doe_cap->write_mbox[doe_cap->write_mbox_len] = val;
360*5fb52f6cSHuai-Cheng Kuo         doe_cap->write_mbox_len++;
361*5fb52f6cSHuai-Cheng Kuo         break;
362*5fb52f6cSHuai-Cheng Kuo     case PCI_EXP_DOE_CAP:
363*5fb52f6cSHuai-Cheng Kuo         /* fallthrough */
364*5fb52f6cSHuai-Cheng Kuo     default:
365*5fb52f6cSHuai-Cheng Kuo         break;
366*5fb52f6cSHuai-Cheng Kuo     }
367*5fb52f6cSHuai-Cheng Kuo }
368