Skip to content

Commit 6ffb163

Browse files
tnmyshJassi Brar
authored andcommitted
mailbox: zynqmp: handle SGI for shared IPI
At least one IPI is used in TF-A for communication with PMC firmware. If this IPI needs to be used by other agents such as RPU then, IPI system interrupt can't be generated in mailbox driver. In such case TF-A generates SGI to mailbox driver for IPI notification. Signed-off-by: Tanmay Shah <[email protected]> Signed-off-by: Saeed Nowshadi <[email protected]> Signed-off-by: Jassi Brar <[email protected]>
1 parent ca1a868 commit 6ffb163

File tree

1 file changed

+152
-7
lines changed

1 file changed

+152
-7
lines changed

drivers/mailbox/zynqmp-ipi-mailbox.c

Lines changed: 152 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@
66
*/
77

88
#include <linux/arm-smccc.h>
9+
#include <linux/cpuhotplug.h>
910
#include <linux/delay.h>
1011
#include <linux/device.h>
1112
#include <linux/interrupt.h>
13+
#include <linux/irqdomain.h>
1214
#include <linux/io.h>
1315
#include <linux/kernel.h>
1416
#include <linux/mailbox_controller.h>
1517
#include <linux/mailbox/zynqmp-ipi-message.h>
1618
#include <linux/module.h>
1719
#include <linux/of.h>
1820
#include <linux/of_address.h>
21+
#include <linux/of_irq.h>
1922
#include <linux/platform_device.h>
2023

2124
/* IPI agent ID any */
@@ -59,6 +62,8 @@
5962
#define DST_BIT_POS 9U
6063
#define SRC_BITMASK GENMASK(11, 8)
6164

65+
#define MAX_SGI 16
66+
6267
/**
6368
* struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel
6469
* @is_opened: indicate if the IPI channel is opened
@@ -111,6 +116,7 @@ struct zynqmp_ipi_mbox {
111116
* @irq: IPI agent interrupt ID
112117
* @method: IPI SMC or HVC is going to be used
113118
* @local_id: local IPI agent ID
119+
* @virq_sgi: IRQ number mapped to SGI
114120
* @num_mboxes: number of mailboxes of this IPI agent
115121
* @ipi_mboxes: IPI mailboxes of this IPI agent
116122
*/
@@ -119,10 +125,13 @@ struct zynqmp_ipi_pdata {
119125
int irq;
120126
unsigned int method;
121127
u32 local_id;
128+
int virq_sgi;
122129
int num_mboxes;
123130
struct zynqmp_ipi_mbox ipi_mboxes[] __counted_by(num_mboxes);
124131
};
125132

133+
static DEFINE_PER_CPU(struct zynqmp_ipi_pdata *, per_cpu_pdata);
134+
126135
static struct device_driver zynqmp_ipi_mbox_driver = {
127136
.owner = THIS_MODULE,
128137
.name = "zynqmp-ipi-mbox",
@@ -189,6 +198,14 @@ static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data)
189198
return status;
190199
}
191200

201+
static irqreturn_t zynqmp_sgi_interrupt(int irq, void *data)
202+
{
203+
struct zynqmp_ipi_pdata **pdata_ptr = data;
204+
struct zynqmp_ipi_pdata *pdata = *pdata_ptr;
205+
206+
return zynqmp_ipi_interrupt(irq, pdata);
207+
}
208+
192209
/**
193210
* zynqmp_ipi_peek_data - Peek to see if there are any rx messages.
194211
*
@@ -748,6 +765,112 @@ static int versal_ipi_setup(struct zynqmp_ipi_mbox *ipi_mbox,
748765
return 0;
749766
}
750767

768+
static int xlnx_mbox_cpuhp_start(unsigned int cpu)
769+
{
770+
struct zynqmp_ipi_pdata *pdata;
771+
772+
pdata = get_cpu_var(per_cpu_pdata);
773+
put_cpu_var(per_cpu_pdata);
774+
enable_percpu_irq(pdata->virq_sgi, IRQ_TYPE_NONE);
775+
776+
return 0;
777+
}
778+
779+
static int xlnx_mbox_cpuhp_down(unsigned int cpu)
780+
{
781+
struct zynqmp_ipi_pdata *pdata;
782+
783+
pdata = get_cpu_var(per_cpu_pdata);
784+
put_cpu_var(per_cpu_pdata);
785+
disable_percpu_irq(pdata->virq_sgi);
786+
787+
return 0;
788+
}
789+
790+
static void xlnx_disable_percpu_irq(void *data)
791+
{
792+
struct zynqmp_ipi_pdata *pdata;
793+
794+
pdata = *this_cpu_ptr(&per_cpu_pdata);
795+
796+
disable_percpu_irq(pdata->virq_sgi);
797+
}
798+
799+
static int xlnx_mbox_init_sgi(struct platform_device *pdev,
800+
int sgi_num,
801+
struct zynqmp_ipi_pdata *pdata)
802+
{
803+
int ret = 0;
804+
int cpu;
805+
806+
/*
807+
* IRQ related structures are used for the following:
808+
* for each SGI interrupt ensure its mapped by GIC IRQ domain
809+
* and that each corresponding linux IRQ for the HW IRQ has
810+
* a handler for when receiving an interrupt from the remote
811+
* processor.
812+
*/
813+
struct irq_domain *domain;
814+
struct irq_fwspec sgi_fwspec;
815+
struct device_node *interrupt_parent = NULL;
816+
struct device *dev = &pdev->dev;
817+
818+
/* Find GIC controller to map SGIs. */
819+
interrupt_parent = of_irq_find_parent(dev->of_node);
820+
if (!interrupt_parent) {
821+
dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n");
822+
return -EINVAL;
823+
}
824+
825+
/* Each SGI needs to be associated with GIC's IRQ domain. */
826+
domain = irq_find_host(interrupt_parent);
827+
of_node_put(interrupt_parent);
828+
829+
/* Each mapping needs GIC domain when finding IRQ mapping. */
830+
sgi_fwspec.fwnode = domain->fwnode;
831+
832+
/*
833+
* When irq domain looks at mapping each arg is as follows:
834+
* 3 args for: interrupt type (SGI), interrupt # (set later), type
835+
*/
836+
sgi_fwspec.param_count = 1;
837+
838+
/* Set SGI's hwirq */
839+
sgi_fwspec.param[0] = sgi_num;
840+
pdata->virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec);
841+
842+
for_each_possible_cpu(cpu)
843+
per_cpu(per_cpu_pdata, cpu) = pdata;
844+
845+
ret = request_percpu_irq(pdata->virq_sgi, zynqmp_sgi_interrupt, pdev->name,
846+
&per_cpu_pdata);
847+
WARN_ON(ret);
848+
if (ret) {
849+
irq_dispose_mapping(pdata->virq_sgi);
850+
return ret;
851+
}
852+
853+
irq_to_desc(pdata->virq_sgi);
854+
irq_set_status_flags(pdata->virq_sgi, IRQ_PER_CPU);
855+
856+
/* Setup function for the CPU hot-plug cases */
857+
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mailbox/sgi:starting",
858+
xlnx_mbox_cpuhp_start, xlnx_mbox_cpuhp_down);
859+
860+
return ret;
861+
}
862+
863+
static void xlnx_mbox_cleanup_sgi(struct zynqmp_ipi_pdata *pdata)
864+
{
865+
cpuhp_remove_state(CPUHP_AP_ONLINE_DYN);
866+
867+
on_each_cpu(xlnx_disable_percpu_irq, NULL, 1);
868+
869+
irq_clear_status_flags(pdata->virq_sgi, IRQ_PER_CPU);
870+
free_percpu_irq(pdata->virq_sgi, &per_cpu_pdata);
871+
irq_dispose_mapping(pdata->virq_sgi);
872+
}
873+
751874
/**
752875
* zynqmp_ipi_free_mboxes - Free IPI mailboxes devices
753876
*
@@ -758,6 +881,9 @@ static void zynqmp_ipi_free_mboxes(struct zynqmp_ipi_pdata *pdata)
758881
struct zynqmp_ipi_mbox *ipi_mbox;
759882
int i;
760883

884+
if (pdata->irq < MAX_SGI)
885+
xlnx_mbox_cleanup_sgi(pdata);
886+
761887
i = pdata->num_mboxes;
762888
for (; i >= 0; i--) {
763889
ipi_mbox = &pdata->ipi_mboxes[i];
@@ -773,7 +899,8 @@ static int zynqmp_ipi_probe(struct platform_device *pdev)
773899
{
774900
struct device *dev = &pdev->dev;
775901
struct device_node *nc, *np = pdev->dev.of_node;
776-
struct zynqmp_ipi_pdata *pdata;
902+
struct zynqmp_ipi_pdata __percpu *pdata;
903+
struct of_phandle_args out_irq;
777904
struct zynqmp_ipi_mbox *mbox;
778905
int num_mboxes, ret = -EINVAL;
779906
setup_ipi_fn ipi_fn;
@@ -821,14 +948,32 @@ static int zynqmp_ipi_probe(struct platform_device *pdev)
821948
mbox++;
822949
}
823950

824-
/* IPI IRQ */
825-
ret = platform_get_irq(pdev, 0);
826-
if (ret < 0)
951+
ret = of_irq_parse_one(dev_of_node(dev), 0, &out_irq);
952+
if (ret < 0) {
953+
dev_err(dev, "failed to parse interrupts\n");
827954
goto free_mbox_dev;
955+
}
956+
ret = out_irq.args[1];
957+
958+
/*
959+
* If Interrupt number is in SGI range, then request SGI else request
960+
* IPI system IRQ.
961+
*/
962+
if (ret < MAX_SGI) {
963+
pdata->irq = ret;
964+
ret = xlnx_mbox_init_sgi(pdev, pdata->irq, pdata);
965+
if (ret)
966+
goto free_mbox_dev;
967+
} else {
968+
ret = platform_get_irq(pdev, 0);
969+
if (ret < 0)
970+
goto free_mbox_dev;
971+
972+
pdata->irq = ret;
973+
ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt,
974+
IRQF_SHARED, dev_name(dev), pdata);
975+
}
828976

829-
pdata->irq = ret;
830-
ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt,
831-
IRQF_SHARED, dev_name(dev), pdata);
832977
if (ret) {
833978
dev_err(dev, "IRQ %d is not requested successfully.\n",
834979
pdata->irq);

0 commit comments

Comments
 (0)