/* Max data object length is 2^18 dwords */ #define PCI_DOE_MAX_LENGTH (1 << 18)
/** * struct pci_doe_mb - State for a single DOE mailbox * * This state is used to manage a single DOE mailbox capability. All fields * should be considered opaque to the consumers and the structure passed into * the helpers below after being created by pci_doe_create_mb(). * * @pdev: PCI device this mailbox belongs to * @cap_offset: Capability offset * @feats: Array of features supported (encoded as long values) * @wq: Wait queue for work item * @work_queue: Queue of pci_doe_work items * @flags: Bit array of PCI_DOE_FLAG_* flags * @sysfs_attrs: Array of sysfs device attributes
*/ struct pci_doe_mb { struct pci_dev *pdev;
u16 cap_offset; struct xarray feats;
/** * struct pci_doe_task - represents a single query/response * * @feat: DOE Feature * @request_pl: The request payload * @request_pl_sz: Size of the request payload (bytes) * @response_pl: The response payload * @response_pl_sz: Size of the response payload (bytes) * @rv: Return value. Length of received response or error (bytes) * @complete: Called when task is complete * @private: Private data for the consumer * @work: Used internally by the mailbox * @doe_mb: Used internally by the mailbox
*/ struct pci_doe_task { struct pci_doe_feature feat; const __le32 *request_pl;
size_t request_pl_sz;
__le32 *response_pl;
size_t response_pl_sz; int rv; void (*complete)(struct pci_doe_task *task); void *private;
/* * Check the DOE busy bit is not set. If it is set, this could indicate * someone other than Linux (e.g. firmware) is using the mailbox. Note * it is expected that firmware and OS will negotiate access rights via * an, as yet to be defined, method. * * Wait up to one PCI_DOE_TIMEOUT period to allow the prior command to * finish. Otherwise, simply error out as unable to field the request. * * PCIe r6.2 sec 6.30.3 states no interrupt is raised when the DOE Busy * bit is cleared, so polling here is our best option for the moment.
*/
timeout_jiffies = jiffies + PCI_DOE_TIMEOUT; do {
pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val);
} while (FIELD_GET(PCI_DOE_STATUS_BUSY, val) &&
!time_after(jiffies, timeout_jiffies));
if (FIELD_GET(PCI_DOE_STATUS_BUSY, val)) return -EBUSY;
if (FIELD_GET(PCI_DOE_STATUS_ERROR, val)) return -EIO;
/* Length is 2 DW of header + length of payload in DW */
length = 2 + DIV_ROUND_UP(task->request_pl_sz, sizeof(__le32)); if (length > PCI_DOE_MAX_LENGTH) return -EIO; if (length == PCI_DOE_MAX_LENGTH)
length = 0;
staticint pci_doe_recv_resp(struct pci_doe_mb *doe_mb, struct pci_doe_task *task)
{
size_t length, payload_length, remainder, received; struct pci_dev *pdev = doe_mb->pdev; int offset = doe_mb->cap_offset; int i = 0;
u32 val;
/* Read the first dword to get the feature */
pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val); if ((FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, val) != task->feat.vid) ||
(FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, val) != task->feat.type)) {
dev_err_ratelimited(&pdev->dev, "[%x] expected [VID, Feature] = [%04x, %02x], got [%04x, %02x]\n",
doe_mb->cap_offset, task->feat.vid, task->feat.type,
FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, val),
FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, val)); return -EIO;
}
pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0); /* Read the second dword to get the length */
pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val);
pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0);
length = FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, val); /* A value of 0x0 indicates max data object length */ if (!length)
length = PCI_DOE_MAX_LENGTH; if (length < 2) return -EIO;
/* First 2 dwords have already been read */
length -= 2;
received = task->response_pl_sz;
payload_length = DIV_ROUND_UP(task->response_pl_sz, sizeof(__le32));
remainder = task->response_pl_sz % sizeof(__le32);
/* remainder signifies number of data bytes in last payload dword */ if (!remainder)
remainder = sizeof(__le32);
if (length < payload_length) {
received = length * sizeof(__le32);
payload_length = length;
remainder = sizeof(__le32);
}
if (payload_length) { /* Read all payload dwords except the last */ for (; i < payload_length - 1; i++) {
pci_read_config_dword(pdev, offset + PCI_DOE_READ,
&val);
task->response_pl[i] = cpu_to_le32(val);
pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0);
}
/* Read last payload dword */
pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val);
cpu_to_le32s(&val);
memcpy(&task->response_pl[i], &val, remainder); /* Prior to the last ack, ensure Data Object Ready */ if (!pci_doe_data_obj_ready(doe_mb)) return -EIO;
pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0);
i++;
}
/* Final error check to pick up on any since Data Object Ready */
pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val); if (FIELD_GET(PCI_DOE_STATUS_ERROR, val)) return -EIO;
if (pci_doe_abort(doe_mb)) { /* * If the device can't process an abort; set the mailbox dead * - no more submissions
*/
pci_err(pdev, "[%x] Abort failed marking mailbox dead\n",
doe_mb->cap_offset);
set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags);
}
signal_task_complete(task, rv);
}
if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) {
signal_task_complete(task, -EIO); return;
}
/* Send request */
rc = pci_doe_send_req(doe_mb, task); if (rc) { /* * The specification does not provide any guidance on how to * resolve conflicting requests from other entities. * Furthermore, it is likely that busy will not be detected * most of the time. Flag any detection of status busy with an * error.
*/ if (rc == -EBUSY)
dev_err_ratelimited(&pdev->dev, "[%x] busy detected; another entity is sending conflicting requests\n",
offset);
signal_task_abort(task, rc); return;
}
rc = xa_insert(&doe_mb->feats, xa_idx++,
pci_doe_xa_feat_entry(vid, type), GFP_KERNEL); if (rc) return rc;
} while (index);
return 0;
}
staticvoid pci_doe_cancel_tasks(struct pci_doe_mb *doe_mb)
{ /* Stop all pending work items from starting */
set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags);
/* Cancel an in progress work item, if necessary */
set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
wake_up(&doe_mb->wq);
}
/** * pci_doe_create_mb() - Create a DOE mailbox object * * @pdev: PCI device to create the DOE mailbox for * @cap_offset: Offset of the DOE mailbox * * Create a single mailbox object to manage the mailbox feature at the * cap_offset specified. * * RETURNS: created mailbox object on success * ERR_PTR(-errno) on failure
*/ staticstruct pci_doe_mb *pci_doe_create_mb(struct pci_dev *pdev,
u16 cap_offset)
{ struct pci_doe_mb *doe_mb; int rc;
doe_mb = kzalloc(sizeof(*doe_mb), GFP_KERNEL); if (!doe_mb) return ERR_PTR(-ENOMEM);
doe_mb->work_queue = alloc_ordered_workqueue("%s %s DOE [%x]", 0,
dev_bus_name(&pdev->dev),
pci_name(pdev),
doe_mb->cap_offset); if (!doe_mb->work_queue) {
pci_err(pdev, "[%x] failed to allocate work queue\n",
doe_mb->cap_offset);
rc = -ENOMEM; goto err_free;
}
/* Reset the mailbox by issuing an abort */
rc = pci_doe_abort(doe_mb); if (rc) {
pci_err(pdev, "[%x] failed to reset mailbox with abort command : %d\n",
doe_mb->cap_offset, rc); goto err_destroy_wq;
}
/* * The state machine and the mailbox should be in sync now; * Use the mailbox to query features.
*/
rc = pci_doe_cache_features(doe_mb); if (rc) {
pci_err(pdev, "[%x] failed to cache features : %d\n",
doe_mb->cap_offset, rc); goto err_cancel;
}
/** * pci_doe_destroy_mb() - Destroy a DOE mailbox object * * @doe_mb: DOE mailbox * * Destroy all internal data structures created for the DOE mailbox.
*/ staticvoid pci_doe_destroy_mb(struct pci_doe_mb *doe_mb)
{
pci_doe_cancel_tasks(doe_mb);
xa_destroy(&doe_mb->feats);
destroy_workqueue(doe_mb->work_queue);
kfree(doe_mb);
}
/** * pci_doe_supports_feat() - Return if the DOE instance supports the given * feature * @doe_mb: DOE mailbox capability to query * @vid: Feature Vendor ID * @type: Feature type * * RETURNS: True if the DOE mailbox supports the feature specified
*/ staticbool pci_doe_supports_feat(struct pci_doe_mb *doe_mb, u16 vid, u8 type)
{ unsignedlong index; void *entry;
/* The discovery feature must always be supported */ if (vid == PCI_VENDOR_ID_PCI_SIG && type == PCI_DOE_FEATURE_DISCOVERY) returntrue;
xa_for_each(&doe_mb->feats, index, entry) if (entry == pci_doe_xa_feat_entry(vid, type)) returntrue;
returnfalse;
}
/** * pci_doe_submit_task() - Submit a task to be processed by the state machine * * @doe_mb: DOE mailbox capability to submit to * @task: task to be queued * * Submit a DOE task (request/response) to the DOE mailbox to be processed. * Returns upon queueing the task object. If the queue is full this function * will sleep until there is room in the queue. * * task->complete will be called when the state machine is done processing this * task. * * @task must be allocated on the stack. * * Excess data will be discarded. * * RETURNS: 0 when task has been successfully queued, -ERRNO on error
*/ staticint pci_doe_submit_task(struct pci_doe_mb *doe_mb, struct pci_doe_task *task)
{ if (!pci_doe_supports_feat(doe_mb, task->feat.vid, task->feat.type)) return -EINVAL;
if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) return -EIO;
/** * pci_doe() - Perform Data Object Exchange * * @doe_mb: DOE Mailbox * @vendor: Vendor ID * @type: Data Object Type * @request: Request payload * @request_sz: Size of request payload (bytes) * @response: Response payload * @response_sz: Size of response payload (bytes) * * Submit @request to @doe_mb and store the @response. * The DOE exchange is performed synchronously and may therefore sleep. * * Payloads are treated as opaque byte streams which are transmitted verbatim, * without byte-swapping. If payloads contain little-endian register values, * the caller is responsible for conversion with cpu_to_le32() / le32_to_cpu(). * * For convenience, arbitrary payload sizes are allowed even though PCIe r6.0 * sec 6.30.1 specifies the Data Object Header 2 "Length" in dwords. The last * (partial) dword is copied with byte granularity and padded with zeroes if * necessary. Callers are thus relieved of using dword-sized bounce buffers. * * RETURNS: Length of received response or negative errno. * Received data in excess of @response_sz is discarded. * The length may be smaller than @response_sz and the caller * is responsible for checking that.
*/ int pci_doe(struct pci_doe_mb *doe_mb, u16 vendor, u8 type, constvoid *request, size_t request_sz, void *response, size_t response_sz)
{
DECLARE_COMPLETION_ONSTACK(c); struct pci_doe_task task = {
.feat.vid = vendor,
.feat.type = type,
.request_pl = request,
.request_pl_sz = request_sz,
.response_pl = response,
.response_pl_sz = response_sz,
.complete = pci_doe_task_complete,
.private = &c,
}; int rc;
rc = pci_doe_submit_task(doe_mb, &task); if (rc) return rc;
wait_for_completion(&c);
return task.rv;
}
EXPORT_SYMBOL_GPL(pci_doe);
/** * pci_find_doe_mailbox() - Find Data Object Exchange mailbox * * @pdev: PCI device * @vendor: Vendor ID * @type: Data Object Type * * Find first DOE mailbox of a PCI device which supports the given feature. * * RETURNS: Pointer to the DOE mailbox or NULL if none was found.
*/ struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
u8 type)
{ struct pci_doe_mb *doe_mb; unsignedlong index;
xa_for_each(&pdev->doe_mbs, index, doe_mb) if (pci_doe_supports_feat(doe_mb, vendor, type)) return doe_mb;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.