/* IPI SMC Macros */ #define IPI_SMC_ENQUIRY_DIRQ_MASK 0x00000001UL /* Flag to indicate if * notification interrupt * to be disabled.
*/ #define IPI_SMC_ACK_EIRQ_MASK 0x00000001UL /* Flag to indicate if * notification interrupt * to be enabled.
*/
/** * struct zynqmp_ipi_mbox - Description of a ZynqMP IPI mailbox * platform data. * @pdata: pointer to the IPI private data * @dev: device pointer corresponding to the Xilinx ZynqMP * IPI mailbox * @remote_id: remote IPI agent ID * @mbox: mailbox Controller * @mchans: array for channels, tx channel and rx channel. * @setup_ipi_fn: Function Pointer to set up IPI Channels
*/ struct zynqmp_ipi_mbox { struct zynqmp_ipi_pdata *pdata; struct device dev;
u32 remote_id; struct mbox_controller mbox; struct zynqmp_ipi_mchan mchans[2];
setup_ipi_fn setup_ipi_fn;
};
/** * struct zynqmp_ipi_pdata - Description of z ZynqMP IPI agent platform data. * * @dev: device pointer corresponding to the Xilinx ZynqMP * IPI agent * @irq: IPI agent interrupt ID * @irq_type: IPI SGI or SPI IRQ type * @method: IPI SMC or HVC is going to be used * @local_id: local IPI agent ID * @virq_sgi: IRQ number mapped to SGI * @num_mboxes: number of mailboxes of this IPI agent * @ipi_mboxes: IPI mailboxes of this IPI agent
*/ struct zynqmp_ipi_pdata { struct device *dev; int irq; unsignedint irq_type; unsignedint method;
u32 local_id; int virq_sgi; int num_mboxes; struct zynqmp_ipi_mbox ipi_mboxes[] __counted_by(num_mboxes);
};
/** * zynqmp_ipi_peek_data - Peek to see if there are any rx messages. * * @chan: Channel Pointer * * Return: 'true' if there is pending rx data, 'false' if there is none.
*/ staticbool zynqmp_ipi_peek_data(struct mbox_chan *chan)
{ struct device *dev = chan->mbox->dev; struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); struct zynqmp_ipi_mchan *mchan = chan->con_priv; int ret;
u64 arg0; struct arm_smccc_res res;
if (WARN_ON(!ipi_mbox)) {
dev_err(dev, "no platform drv data??\n"); returnfalse;
}
if (mchan->chan_type == IPI_MB_CHNL_TX) { /* TX channel, check if the message has been acked * by the remote, if yes, response is available.
*/ if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING) returnfalse; else returntrue;
} elseif (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) { /* RX channel, check if there is message arrived. */ returntrue;
} returnfalse;
}
/** * zynqmp_ipi_last_tx_done - See if the last tx message is sent * * @chan: Channel pointer * * Return: 'true' is no pending tx data, 'false' if there are any.
*/ staticbool zynqmp_ipi_last_tx_done(struct mbox_chan *chan)
{ struct device *dev = chan->mbox->dev; struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); struct zynqmp_ipi_mchan *mchan = chan->con_priv; int ret;
u64 arg0; struct arm_smccc_res res;
if (WARN_ON(!ipi_mbox)) {
dev_err(dev, "no platform drv data??\n"); returnfalse;
}
if (mchan->chan_type == IPI_MB_CHNL_TX) { /* We only need to check if the message been taken * by the remote in the TX channel
*/
arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY;
zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); /* Check the SMC call status, a0 of the result */
ret = (int)(res.a0 & 0xFFFFFFFF); if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING) returnfalse; returntrue;
} /* Always true for the response message in RX channel */ returntrue;
}
/* If no channel has been opened, open the IPI mailbox */
nchan_type = (mchan->chan_type + 1) % 2; if (!ipi_mbox->mchans[nchan_type].is_opened) {
arg0 = SMC_IPI_MAILBOX_OPEN;
zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); /* Check the SMC call status, a0 of the result */
ret = (int)(res.a0 & 0xFFFFFFFF); if (ret < 0) {
dev_err(dev, "SMC to open the IPI channel failed.\n"); return ret;
}
ret = 0;
}
/* If it is RX channel, enable the IPI notification interrupt */ if (mchan->chan_type == IPI_MB_CHNL_RX) {
arg0 = SMC_IPI_MAILBOX_ENABLE_IRQ;
zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res);
}
mchan->is_opened = 1;
/* Only supports TX and RX channels */
chan_type = p->args[0]; if (chan_type != IPI_MB_CHNL_TX && chan_type != IPI_MB_CHNL_RX) {
dev_err(dev, "req chnl failure: invalid chnl type %u.\n",
chan_type); return ERR_PTR(-EINVAL);
}
chan = &mbox->chans[chan_type]; return chan;
}
/** * zynqmp_ipi_mbox_get_buf_res - Get buffer resource from the IPI dev node * * @node: IPI mbox device child node * @name: name of the IPI buffer * @res: pointer to where the resource information will be stored. * * Return: 0 for success, negative value for failure
*/ staticint zynqmp_ipi_mbox_get_buf_res(struct device_node *node, constchar *name, struct resource *res)
{ int ret, index;
index = of_property_match_string(node, "reg-names", name); if (index >= 0) {
ret = of_address_to_resource(node, index, res); if (ret < 0) return -EINVAL; return 0;
} return -ENODEV;
}
/** * zynqmp_ipi_mbox_dev_release() - release the existence of a ipi mbox dev * * @dev: the ipi mailbox device * * This is to avoid the no device release() function kernel warning. *
*/ staticvoid zynqmp_ipi_mbox_dev_release(struct device *dev)
{
(void)dev;
}
/** * zynqmp_ipi_mbox_probe - probe IPI mailbox resource from device node * * @ipi_mbox: pointer to IPI mailbox private data structure * @node: IPI mailbox device node * * Return: 0 for success, negative value for failure
*/ staticint zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, struct device_node *node)
{ struct mbox_chan *chans; struct mbox_controller *mbox; struct device *dev, *mdev; int ret;
dev = ipi_mbox->pdata->dev; /* Initialize dev for IPI mailbox */
ipi_mbox->dev.parent = dev;
ipi_mbox->dev.release = NULL;
ipi_mbox->dev.of_node = node;
dev_set_name(&ipi_mbox->dev, "%s", of_node_full_name(node));
dev_set_drvdata(&ipi_mbox->dev, ipi_mbox);
ipi_mbox->dev.release = zynqmp_ipi_mbox_dev_release;
ipi_mbox->dev.driver = &zynqmp_ipi_mbox_driver;
ret = device_register(&ipi_mbox->dev); if (ret) {
dev_err(dev, "Failed to register ipi mbox dev.\n");
put_device(&ipi_mbox->dev); return ret;
}
mdev = &ipi_mbox->dev;
/* Get the IPI remote agent ID */
ret = of_property_read_u32(node, "xlnx,ipi-id", &ipi_mbox->remote_id); if (ret < 0) {
dev_err(dev, "No IPI remote ID is specified.\n"); return ret;
}
ret = ipi_mbox->setup_ipi_fn(ipi_mbox, node); if (ret) {
dev_err(dev, "Failed to set up IPI Buffers.\n"); return ret;
}
/** * zynqmp_ipi_setup - set up IPI Buffers for classic flow * * @ipi_mbox: pointer to IPI mailbox private data structure * @node: IPI mailbox device node * * This will be used to set up IPI Buffers for ZynqMP SOC if user * wishes to use classic driver usage model on new SOC's with only * buffered IPIs. * * Note that bufferless IPIs and mixed usage of buffered and bufferless * IPIs are not supported with this flow. * * This will be invoked with compatible string "xlnx,zynqmp-ipi-mailbox". * * Return: 0 for success, negative value for failure
*/ staticint zynqmp_ipi_setup(struct zynqmp_ipi_mbox *ipi_mbox, struct device_node *node)
{ struct zynqmp_ipi_mchan *mchan; struct device *mdev; struct resource res; constchar *name; int ret;
/* * Only set up buffers if both sides claim to have msg buffers. * This is because each buffered IPI's corresponding msg buffers * are reserved for use by other buffered IPI's.
*/ if (!host_idx && !remote_idx) {
u32 host_src, host_dst, remote_src, remote_dst;
u32 buff_sz;
staticint xlnx_mbox_init_sgi(struct platform_device *pdev, int sgi_num, struct zynqmp_ipi_pdata *pdata)
{ int ret = 0; int cpu;
/* * IRQ related structures are used for the following: * for each SGI interrupt ensure its mapped by GIC IRQ domain * and that each corresponding linux IRQ for the HW IRQ has * a handler for when receiving an interrupt from the remote * processor.
*/ struct irq_domain *domain; struct irq_fwspec sgi_fwspec; struct device_node *interrupt_parent = NULL; struct device *dev = &pdev->dev;
/* Find GIC controller to map SGIs. */
interrupt_parent = of_irq_find_parent(dev->of_node); if (!interrupt_parent) {
dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n"); return -EINVAL;
}
/* Each SGI needs to be associated with GIC's IRQ domain. */
domain = irq_find_host(interrupt_parent);
of_node_put(interrupt_parent);
/* Each mapping needs GIC domain when finding IRQ mapping. */
sgi_fwspec.fwnode = domain->fwnode;
/* * When irq domain looks at mapping each arg is as follows: * 3 args for: interrupt type (SGI), interrupt # (set later), type
*/
sgi_fwspec.param_count = 1;
/* Setup function for the CPU hot-plug cases */
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mailbox/sgi:starting",
xlnx_mbox_cpuhp_start, xlnx_mbox_cpuhp_down);
if (pdata->irq_type == IPI_IRQ_TYPE_SGI)
xlnx_mbox_cleanup_sgi(pdata);
i = pdata->num_mboxes - 1; for (; i >= 0; i--) {
ipi_mbox = &pdata->ipi_mboxes[i]; if (device_is_registered(&ipi_mbox->dev))
device_unregister(&ipi_mbox->dev);
}
}
/* Get the IPI local agents ID */
ret = of_property_read_u32(np, "xlnx,ipi-id", &pdata->local_id); if (ret < 0) {
dev_err(dev, "No IPI local ID is specified.\n"); return ret;
}
ipi_fn = (setup_ipi_fn)device_get_match_data(&pdev->dev); if (!ipi_fn) {
dev_err(dev, "Mbox Compatible String is missing IPI Setup fn.\n"); return -ENODEV;
}
ret = zynqmp_ipi_mbox_probe(mbox, nc); if (ret) {
of_node_put(nc);
dev_err(dev, "failed to probe subdev.\n");
ret = -EINVAL; goto free_mbox_dev;
}
mbox++;
}
ret = of_irq_parse_one(dev_of_node(dev), 0, &out_irq); if (ret < 0) {
dev_err(dev, "failed to parse interrupts\n"); goto free_mbox_dev;
}
/* Use interrupt type to distinguish SGI and SPI interrupts */
pdata->irq_type = out_irq.args[0];
/* * If Interrupt number is in SGI range, then request SGI else request * IPI system IRQ.
*/ if (pdata->irq_type == IPI_IRQ_TYPE_SGI) {
pdata->irq = out_irq.args[1];
ret = xlnx_mbox_init_sgi(pdev, pdata->irq, pdata); if (ret) goto free_mbox_dev;
} else {
ret = platform_get_irq(pdev, 0); if (ret < 0) goto free_mbox_dev;
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.