/* Limit CCP use to a specifed number of queues per device */ staticunsignedint nqueues;
module_param(nqueues, uint, 0444);
MODULE_PARM_DESC(nqueues, "Number of queues per CCP (minimum 1; default: all available)");
/* Limit the maximum number of configured CCPs */ static atomic_t dev_count = ATOMIC_INIT(0); staticunsignedint max_devs = MAX_CCPS;
module_param(max_devs, uint, 0444);
MODULE_PARM_DESC(max_devs, "Maximum number of CCPs to enable (default: all; 0 disables all CCPs)");
if (e < ARRAY_SIZE(ccp_error_codes))
dev_err(d->dev, "CCP error %d: %s\n", e, ccp_error_codes[e]); else
dev_err(d->dev, "CCP error %d: Unknown Error\n", e);
}
/* List of CCPs, CCP count, read-write access lock, and access functions * * Lock structure: get ccp_unit_lock for reading whenever we need to * examine the CCP list. While holding it for reading we can acquire * the RR lock to update the round-robin next-CCP pointer. The unit lock * must be acquired before the RR lock. * * If the unit-lock is acquired for writing, we have total control over * the list, so there's no value in getting the RR lock.
*/ static DEFINE_RWLOCK(ccp_unit_lock); static LIST_HEAD(ccp_units);
/** * ccp_add_device - add a CCP device to the list * * @ccp: ccp_device struct pointer * * Put this CCP on the unit list, which makes it available * for use. * * Returns zero if a CCP device is present, -ENODEV otherwise.
*/ void ccp_add_device(struct ccp_device *ccp)
{ unsignedlong flags;
write_lock_irqsave(&ccp_unit_lock, flags);
list_add_tail(&ccp->entry, &ccp_units); if (!ccp_rr) /* We already have the list lock (we're first) so this * pointer can't change on us. Set its initial value.
*/
ccp_rr = ccp;
write_unlock_irqrestore(&ccp_unit_lock, flags);
}
/** * ccp_del_device - remove a CCP device from the list * * @ccp: ccp_device struct pointer * * Remove this unit from the list of devices. If the next device * up for use is this one, adjust the pointer. If this is the last * device, NULL the pointer.
*/ void ccp_del_device(struct ccp_device *ccp)
{ unsignedlong flags;
write_lock_irqsave(&ccp_unit_lock, flags); if (ccp_rr == ccp) { /* ccp_unit_lock is read/write; any read access * will be suspended while we make changes to the * list and RR pointer.
*/ if (list_is_last(&ccp_rr->entry, &ccp_units))
ccp_rr = list_first_entry(&ccp_units, struct ccp_device,
entry); else
ccp_rr = list_next_entry(ccp_rr, entry);
}
list_del(&ccp->entry); if (list_empty(&ccp_units))
ccp_rr = NULL;
write_unlock_irqrestore(&ccp_unit_lock, flags);
}
int ccp_register_rng(struct ccp_device *ccp)
{ int ret = 0;
dev_dbg(ccp->dev, "Registering RNG...\n"); /* Register an RNG */
ccp->hwrng.name = ccp->rngname;
ccp->hwrng.read = ccp_trng_read;
ret = hwrng_register(&ccp->hwrng); if (ret)
dev_err(ccp->dev, "error registering hwrng (%d)\n", ret);
return ret;
}
void ccp_unregister_rng(struct ccp_device *ccp)
{ if (ccp->hwrng.name)
hwrng_unregister(&ccp->hwrng);
}
/* We round-robin through the unit list. * The (ccp_rr) pointer refers to the next unit to use.
*/
read_lock_irqsave(&ccp_unit_lock, flags); if (!list_empty(&ccp_units)) {
spin_lock(&ccp_rr_lock);
dp = ccp_rr; if (list_is_last(&ccp_rr->entry, &ccp_units))
ccp_rr = list_first_entry(&ccp_units, struct ccp_device,
entry); else
ccp_rr = list_next_entry(ccp_rr, entry);
spin_unlock(&ccp_rr_lock);
}
read_unlock_irqrestore(&ccp_unit_lock, flags);
return dp;
}
/** * ccp_present - check if a CCP device is present * * Returns zero if a CCP device is present, -ENODEV otherwise.
*/ int ccp_present(void)
{ unsignedlong flags; int ret;
read_lock_irqsave(&ccp_unit_lock, flags);
ret = list_empty(&ccp_units);
read_unlock_irqrestore(&ccp_unit_lock, flags);
return ret ? -ENODEV : 0;
}
EXPORT_SYMBOL_GPL(ccp_present);
/** * ccp_version - get the version of the CCP device * * Returns the version from the first unit on the list; * otherwise a zero if no CCP device is present
*/ unsignedint ccp_version(void)
{ struct ccp_device *dp; unsignedlong flags; int ret = 0;
read_lock_irqsave(&ccp_unit_lock, flags); if (!list_empty(&ccp_units)) {
dp = list_first_entry(&ccp_units, struct ccp_device, entry);
ret = dp->vdata->version;
}
read_unlock_irqrestore(&ccp_unit_lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(ccp_version);
/** * ccp_enqueue_cmd - queue an operation for processing by the CCP * * @cmd: ccp_cmd struct to be processed * * Queue a cmd to be processed by the CCP. If queueing the cmd * would exceed the defined length of the cmd queue the cmd will * only be queued if the CCP_CMD_MAY_BACKLOG flag is set and will * result in a return code of -EBUSY. * * The callback routine specified in the ccp_cmd struct will be * called to notify the caller of completion (if the cmd was not * backlogged) or advancement out of the backlog. If the cmd has * advanced out of the backlog the "err" value of the callback * will be -EINPROGRESS. Any other "err" value during callback is * the result of the operation. * * The cmd has been successfully queued if: * the return code is -EINPROGRESS or * the return code is -EBUSY and CCP_CMD_MAY_BACKLOG flag is set
*/ int ccp_enqueue_cmd(struct ccp_cmd *cmd)
{ struct ccp_device *ccp; unsignedlong flags; unsignedint i; int ret;
/* Some commands might need to be sent to a specific device */
ccp = cmd->ccp ? cmd->ccp : ccp_get_device();
if (!ccp) return -ENODEV;
/* Caller must supply a callback routine */ if (!cmd->callback) return -EINVAL;
cmd->ccp = ccp;
spin_lock_irqsave(&ccp->cmd_lock, flags);
i = ccp->cmd_q_count;
if (ccp->cmd_count >= MAX_CMD_QLEN) { if (cmd->flags & CCP_CMD_MAY_BACKLOG) {
ret = -EBUSY;
list_add_tail(&cmd->entry, &ccp->backlog);
} else {
ret = -ENOSPC;
}
} else {
ret = -EINPROGRESS;
ccp->cmd_count++;
list_add_tail(&cmd->entry, &ccp->cmd);
/* Find an idle queue */ if (!ccp->suspending) { for (i = 0; i < ccp->cmd_q_count; i++) { if (ccp->cmd_q[i].active) continue;
break;
}
}
}
spin_unlock_irqrestore(&ccp->cmd_lock, flags);
/* If we found an idle queue, wake it up */ if (i < ccp->cmd_q_count)
wake_up_process(ccp->cmd_q[i].kthread);
int ccp_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{ struct ccp_device *ccp = container_of(rng, struct ccp_device, hwrng);
u32 trng_value; int len = min_t(int, sizeof(trng_value), max);
/* Locking is provided by the caller so we can update device * hwrng-related fields safely
*/
trng_value = ioread32(ccp->io_regs + TRNG_OUT_REG); if (!trng_value) { /* Zero is returned if not data is available or if a * bad-entropy error is present. Assume an error if * we exceed TRNG_RETRIES reads of zero.
*/ if (ccp->hwrng_retries++ > TRNG_RETRIES) return -EIO;
return 0;
}
/* Reset the counter and save the rng value */
ccp->hwrng_retries = 0;
memcpy(data, &trng_value, len);
/* If there's no device there's nothing to do */ if (!ccp) return;
spin_lock_irqsave(&ccp->cmd_lock, flags);
ccp->suspending = 1;
/* Wake all the queue kthreads to prepare for suspend */ for (i = 0; i < ccp->cmd_q_count; i++)
wake_up_process(ccp->cmd_q[i].kthread);
spin_unlock_irqrestore(&ccp->cmd_lock, flags);
/* Wait for all queue kthreads to say they're done */ while (!ccp_queues_suspended(ccp))
wait_event_interruptible(ccp->suspend_queue,
ccp_queues_suspended(ccp));
}
/* If there's no device there's nothing to do */ if (!ccp) return;
spin_lock_irqsave(&ccp->cmd_lock, flags);
ccp->suspending = 0;
/* Wake up all the kthreads */ for (i = 0; i < ccp->cmd_q_count; i++) {
ccp->cmd_q[i].suspended = 0;
wake_up_process(ccp->cmd_q[i].kthread);
}
spin_unlock_irqrestore(&ccp->cmd_lock, flags);
}
int ccp_dev_init(struct sp_device *sp)
{ struct device *dev = sp->dev; struct ccp_device *ccp; int ret;
/* * Check how many we have so far, and stop after reaching * that number
*/ if (atomic_inc_return(&dev_count) > max_devs) return 0; /* don't fail the load */
ret = -ENOMEM;
ccp = ccp_alloc_struct(sp); if (!ccp) goto e_err;
sp->ccp_data = ccp;
ccp->vdata = (struct ccp_vdata *)sp->dev_vdata->ccp_vdata; if (!ccp->vdata || !ccp->vdata->version) {
ret = -ENODEV;
dev_err(dev, "missing driver data\n"); goto e_err;
}
ccp->use_tasklet = sp->use_tasklet;
ccp->io_regs = sp->io_map + ccp->vdata->offset; if (ccp->vdata->setup)
ccp->vdata->setup(ccp);
ret = ccp->vdata->perform->init(ccp); if (ret) { /* A positive number means that the device cannot be initialized, * but no additional message is required.
*/ if (ret > 0) goto e_quiet;
/* An unexpected problem occurred, and should be reported in the log */ goto e_err;
}
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.