1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
6 #include <linux/bitfield.h>
7 #include <linux/interrupt.h>
9 #include <linux/irqdomain.h>
10 #include <linux/mailbox_controller.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
14 #include <dt-bindings/mailbox/qcom-ipcc.h>
16 #define IPCC_MBOX_MAX_CHAN 48
18 /* IPCC Register offsets */
19 #define IPCC_REG_SEND_ID 0x0c
20 #define IPCC_REG_RECV_ID 0x10
21 #define IPCC_REG_RECV_SIGNAL_ENABLE 0x14
22 #define IPCC_REG_RECV_SIGNAL_DISABLE 0x18
23 #define IPCC_REG_RECV_SIGNAL_CLEAR 0x1c
24 #define IPCC_REG_CLIENT_CLEAR 0x38
26 #define IPCC_SIGNAL_ID_MASK GENMASK(15, 0)
27 #define IPCC_CLIENT_ID_MASK GENMASK(31, 16)
29 #define IPCC_NO_PENDING_IRQ GENMASK(31, 0)
32 * struct qcom_ipcc_chan_info - Per-mailbox-channel info
33 * @client_id: The client-id to which the interrupt has to be triggered
34 * @signal_id: The signal-id to which the interrupt has to be triggered
36 struct qcom_ipcc_chan_info
{
42 * struct qcom_ipcc - Holder for the mailbox driver
43 * @dev: Device associated with this instance
44 * @base: Base address of the IPCC frame associated to APSS
45 * @irq_domain: The irq_domain associated with this instance
46 * @chan: The mailbox channels array
47 * @mchan: The per-mailbox channel info array
48 * @mbox: The mailbox controller
54 struct irq_domain
*irq_domain
;
55 struct mbox_chan chan
[IPCC_MBOX_MAX_CHAN
];
56 struct qcom_ipcc_chan_info mchan
[IPCC_MBOX_MAX_CHAN
];
57 struct mbox_controller mbox
;
61 static inline struct qcom_ipcc
*to_qcom_ipcc(struct mbox_controller
*mbox
)
63 return container_of(mbox
, struct qcom_ipcc
, mbox
);
66 static inline u32
qcom_ipcc_get_hwirq(u16 client_id
, u16 signal_id
)
68 return FIELD_PREP(IPCC_CLIENT_ID_MASK
, client_id
) |
69 FIELD_PREP(IPCC_SIGNAL_ID_MASK
, signal_id
);
72 static irqreturn_t
qcom_ipcc_irq_fn(int irq
, void *data
)
74 struct qcom_ipcc
*ipcc
= data
;
79 hwirq
= readl(ipcc
->base
+ IPCC_REG_RECV_ID
);
80 if (hwirq
== IPCC_NO_PENDING_IRQ
)
83 virq
= irq_find_mapping(ipcc
->irq_domain
, hwirq
);
84 writel(hwirq
, ipcc
->base
+ IPCC_REG_RECV_SIGNAL_CLEAR
);
85 generic_handle_irq(virq
);
91 static void qcom_ipcc_mask_irq(struct irq_data
*irqd
)
93 struct qcom_ipcc
*ipcc
= irq_data_get_irq_chip_data(irqd
);
94 irq_hw_number_t hwirq
= irqd_to_hwirq(irqd
);
96 writel(hwirq
, ipcc
->base
+ IPCC_REG_RECV_SIGNAL_DISABLE
);
99 static void qcom_ipcc_unmask_irq(struct irq_data
*irqd
)
101 struct qcom_ipcc
*ipcc
= irq_data_get_irq_chip_data(irqd
);
102 irq_hw_number_t hwirq
= irqd_to_hwirq(irqd
);
104 writel(hwirq
, ipcc
->base
+ IPCC_REG_RECV_SIGNAL_ENABLE
);
107 static struct irq_chip qcom_ipcc_irq_chip
= {
109 .irq_mask
= qcom_ipcc_mask_irq
,
110 .irq_unmask
= qcom_ipcc_unmask_irq
,
111 .flags
= IRQCHIP_SKIP_SET_WAKE
,
114 static int qcom_ipcc_domain_map(struct irq_domain
*d
, unsigned int irq
,
117 struct qcom_ipcc
*ipcc
= d
->host_data
;
119 irq_set_chip_and_handler(irq
, &qcom_ipcc_irq_chip
, handle_level_irq
);
120 irq_set_chip_data(irq
, ipcc
);
121 irq_set_noprobe(irq
);
126 static int qcom_ipcc_domain_xlate(struct irq_domain
*d
,
127 struct device_node
*node
, const u32
*intspec
,
128 unsigned int intsize
,
129 unsigned long *out_hwirq
,
130 unsigned int *out_type
)
135 *out_hwirq
= qcom_ipcc_get_hwirq(intspec
[0], intspec
[1]);
136 *out_type
= intspec
[2] & IRQ_TYPE_SENSE_MASK
;
141 static const struct irq_domain_ops qcom_ipcc_irq_ops
= {
142 .map
= qcom_ipcc_domain_map
,
143 .xlate
= qcom_ipcc_domain_xlate
,
146 static int qcom_ipcc_mbox_send_data(struct mbox_chan
*chan
, void *data
)
148 struct qcom_ipcc
*ipcc
= to_qcom_ipcc(chan
->mbox
);
149 struct qcom_ipcc_chan_info
*mchan
= chan
->con_priv
;
152 hwirq
= qcom_ipcc_get_hwirq(mchan
->client_id
, mchan
->signal_id
);
153 writel(hwirq
, ipcc
->base
+ IPCC_REG_SEND_ID
);
158 static struct mbox_chan
*qcom_ipcc_mbox_xlate(struct mbox_controller
*mbox
,
159 const struct of_phandle_args
*ph
)
161 struct qcom_ipcc
*ipcc
= to_qcom_ipcc(mbox
);
162 struct qcom_ipcc_chan_info
*mchan
;
163 struct mbox_chan
*chan
;
166 if (ph
->args_count
!= 2)
167 return ERR_PTR(-EINVAL
);
169 for (i
= 0; i
< IPCC_MBOX_MAX_CHAN
; i
++) {
170 chan
= &ipcc
->chan
[i
];
171 if (!chan
->con_priv
) {
172 mchan
= &ipcc
->mchan
[i
];
173 mchan
->client_id
= ph
->args
[0];
174 mchan
->signal_id
= ph
->args
[1];
175 chan
->con_priv
= mchan
;
182 return chan
?: ERR_PTR(-EBUSY
);
185 static const struct mbox_chan_ops ipcc_mbox_chan_ops
= {
186 .send_data
= qcom_ipcc_mbox_send_data
,
189 static int qcom_ipcc_setup_mbox(struct qcom_ipcc
*ipcc
)
191 struct mbox_controller
*mbox
;
192 struct device
*dev
= ipcc
->dev
;
196 mbox
->num_chans
= IPCC_MBOX_MAX_CHAN
;
197 mbox
->chans
= ipcc
->chan
;
198 mbox
->ops
= &ipcc_mbox_chan_ops
;
199 mbox
->of_xlate
= qcom_ipcc_mbox_xlate
;
200 mbox
->txdone_irq
= false;
201 mbox
->txdone_poll
= false;
203 return devm_mbox_controller_register(dev
, mbox
);
206 static int qcom_ipcc_probe(struct platform_device
*pdev
)
208 struct qcom_ipcc
*ipcc
;
211 ipcc
= devm_kzalloc(&pdev
->dev
, sizeof(*ipcc
), GFP_KERNEL
);
215 ipcc
->dev
= &pdev
->dev
;
217 ipcc
->base
= devm_platform_ioremap_resource(pdev
, 0);
218 if (IS_ERR(ipcc
->base
))
219 return PTR_ERR(ipcc
->base
);
221 ipcc
->irq
= platform_get_irq(pdev
, 0);
225 ipcc
->irq_domain
= irq_domain_add_tree(pdev
->dev
.of_node
,
226 &qcom_ipcc_irq_ops
, ipcc
);
227 if (!ipcc
->irq_domain
)
230 ret
= qcom_ipcc_setup_mbox(ipcc
);
234 ret
= devm_request_irq(&pdev
->dev
, ipcc
->irq
, qcom_ipcc_irq_fn
,
235 IRQF_TRIGGER_HIGH
, "ipcc", ipcc
);
237 dev_err(&pdev
->dev
, "Failed to register the irq: %d\n", ret
);
241 enable_irq_wake(ipcc
->irq
);
242 platform_set_drvdata(pdev
, ipcc
);
247 irq_domain_remove(ipcc
->irq_domain
);
252 static int qcom_ipcc_remove(struct platform_device
*pdev
)
254 struct qcom_ipcc
*ipcc
= platform_get_drvdata(pdev
);
256 disable_irq_wake(ipcc
->irq
);
257 irq_domain_remove(ipcc
->irq_domain
);
262 static const struct of_device_id qcom_ipcc_of_match
[] = {
263 { .compatible
= "qcom,ipcc"},
266 MODULE_DEVICE_TABLE(of
, qcom_ipcc_of_match
);
268 static struct platform_driver qcom_ipcc_driver
= {
269 .probe
= qcom_ipcc_probe
,
270 .remove
= qcom_ipcc_remove
,
273 .of_match_table
= qcom_ipcc_of_match
,
277 static int __init
qcom_ipcc_init(void)
279 return platform_driver_register(&qcom_ipcc_driver
);
281 arch_initcall(qcom_ipcc_init
);
283 MODULE_AUTHOR("Venkata Narendra Kumar Gutta <vnkgutta@codeaurora.org>");
284 MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
285 MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPCC driver");
286 MODULE_LICENSE("GPL v2");