xref: /linux/drivers/mailbox/arm_mhuv3.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1ca1a8680SCristian Marussi // SPDX-License-Identifier: GPL-2.0
2ca1a8680SCristian Marussi /*
3ca1a8680SCristian Marussi  * ARM Message Handling Unit Version 3 (MHUv3) driver.
4ca1a8680SCristian Marussi  *
5ca1a8680SCristian Marussi  * Copyright (C) 2024 ARM Ltd.
6ca1a8680SCristian Marussi  *
7ca1a8680SCristian Marussi  * Based on ARM MHUv2 driver.
8ca1a8680SCristian Marussi  */
9ca1a8680SCristian Marussi 
10ca1a8680SCristian Marussi #include <linux/bitfield.h>
11ca1a8680SCristian Marussi #include <linux/bitops.h>
12ca1a8680SCristian Marussi #include <linux/bits.h>
13ca1a8680SCristian Marussi #include <linux/cleanup.h>
14ca1a8680SCristian Marussi #include <linux/device.h>
15ca1a8680SCristian Marussi #include <linux/interrupt.h>
16ca1a8680SCristian Marussi #include <linux/mailbox_controller.h>
17ca1a8680SCristian Marussi #include <linux/module.h>
18ca1a8680SCristian Marussi #include <linux/of_address.h>
19ca1a8680SCristian Marussi #include <linux/platform_device.h>
20ca1a8680SCristian Marussi #include <linux/spinlock.h>
21ca1a8680SCristian Marussi #include <linux/sizes.h>
22ca1a8680SCristian Marussi #include <linux/slab.h>
23ca1a8680SCristian Marussi #include <linux/types.h>
24ca1a8680SCristian Marussi 
25ca1a8680SCristian Marussi /* ====== MHUv3 Registers ====== */
26ca1a8680SCristian Marussi 
27ca1a8680SCristian Marussi /* Maximum number of Doorbell channel windows */
28ca1a8680SCristian Marussi #define MHUV3_DBCW_MAX			128
29ca1a8680SCristian Marussi /* Number of DBCH combined interrupt status registers */
30ca1a8680SCristian Marussi #define MHUV3_DBCH_CMB_INT_ST_REG_CNT	4
31ca1a8680SCristian Marussi 
32ca1a8680SCristian Marussi /* Number of FFCH combined interrupt status registers */
33ca1a8680SCristian Marussi #define MHUV3_FFCH_CMB_INT_ST_REG_CNT	2
34ca1a8680SCristian Marussi 
35ca1a8680SCristian Marussi #define MHUV3_FLAG_BITS			32
36ca1a8680SCristian Marussi 
37ca1a8680SCristian Marussi /* Not a typo ... */
38ca1a8680SCristian Marussi #define MHUV3_MAJOR_VERSION		2
39ca1a8680SCristian Marussi 
40ca1a8680SCristian Marussi enum {
41ca1a8680SCristian Marussi 	MHUV3_MBOX_CELL_TYPE,
42ca1a8680SCristian Marussi 	MHUV3_MBOX_CELL_CHWN,
43ca1a8680SCristian Marussi 	MHUV3_MBOX_CELL_PARAM,
44ca1a8680SCristian Marussi 	MHUV3_MBOX_CELLS
45ca1a8680SCristian Marussi };
46ca1a8680SCristian Marussi 
47ca1a8680SCristian Marussi /* Padding bitfields/fields represents hole in the regs MMIO */
48ca1a8680SCristian Marussi 
49ca1a8680SCristian Marussi /* CTRL_Page */
50ca1a8680SCristian Marussi struct blk_id {
51ca1a8680SCristian Marussi #define id		GENMASK(3, 0)
52ca1a8680SCristian Marussi 	u32 val;
53ca1a8680SCristian Marussi } __packed;
54ca1a8680SCristian Marussi 
55ca1a8680SCristian Marussi struct feat_spt0 {
56ca1a8680SCristian Marussi #define	dbe_spt		GENMASK(3, 0)
57ca1a8680SCristian Marussi #define	fe_spt		GENMASK(7, 4)
58ca1a8680SCristian Marussi #define	fce_spt		GENMASK(11, 8)
59ca1a8680SCristian Marussi 	u32 val;
60ca1a8680SCristian Marussi } __packed;
61ca1a8680SCristian Marussi 
62ca1a8680SCristian Marussi struct feat_spt1 {
63ca1a8680SCristian Marussi #define	auto_op_spt	GENMASK(3, 0)
64ca1a8680SCristian Marussi 	u32 val;
65ca1a8680SCristian Marussi } __packed;
66ca1a8680SCristian Marussi 
67ca1a8680SCristian Marussi struct dbch_cfg0 {
68ca1a8680SCristian Marussi #define num_dbch	GENMASK(7, 0)
69ca1a8680SCristian Marussi 	u32 val;
70ca1a8680SCristian Marussi } __packed;
71ca1a8680SCristian Marussi 
72ca1a8680SCristian Marussi struct ffch_cfg0 {
73ca1a8680SCristian Marussi #define num_ffch	GENMASK(7, 0)
74ca1a8680SCristian Marussi #define x8ba_spt	BIT(8)
75ca1a8680SCristian Marussi #define x16ba_spt	BIT(9)
76ca1a8680SCristian Marussi #define x32ba_spt	BIT(10)
77ca1a8680SCristian Marussi #define x64ba_spt	BIT(11)
78ca1a8680SCristian Marussi #define ffch_depth	GENMASK(25, 16)
79ca1a8680SCristian Marussi 	u32 val;
80ca1a8680SCristian Marussi } __packed;
81ca1a8680SCristian Marussi 
82ca1a8680SCristian Marussi struct fch_cfg0 {
83ca1a8680SCristian Marussi #define num_fch		GENMASK(9, 0)
84ca1a8680SCristian Marussi #define fcgi_spt	BIT(10)		// MBX-only
85ca1a8680SCristian Marussi #define num_fcg		GENMASK(15, 11)
86ca1a8680SCristian Marussi #define num_fch_per_grp	GENMASK(20, 16)
87ca1a8680SCristian Marussi #define fch_ws		GENMASK(28, 21)
88ca1a8680SCristian Marussi 	u32 val;
89ca1a8680SCristian Marussi } __packed;
90ca1a8680SCristian Marussi 
91ca1a8680SCristian Marussi struct ctrl {
92ca1a8680SCristian Marussi #define op_req		BIT(0)
93ca1a8680SCristian Marussi #define	ch_op_mask	BIT(1)
94ca1a8680SCristian Marussi 	u32 val;
95ca1a8680SCristian Marussi } __packed;
96ca1a8680SCristian Marussi 
97ca1a8680SCristian Marussi struct fch_ctrl {
98ca1a8680SCristian Marussi #define _int_en		BIT(2)
99ca1a8680SCristian Marussi 	u32 val;
100ca1a8680SCristian Marussi } __packed;
101ca1a8680SCristian Marussi 
102ca1a8680SCristian Marussi struct iidr {
103ca1a8680SCristian Marussi #define implementer	GENMASK(11, 0)
104ca1a8680SCristian Marussi #define revision	GENMASK(15, 12)
105ca1a8680SCristian Marussi #define variant		GENMASK(19, 16)
106ca1a8680SCristian Marussi #define product_id	GENMASK(31, 20)
107ca1a8680SCristian Marussi 	u32 val;
108ca1a8680SCristian Marussi } __packed;
109ca1a8680SCristian Marussi 
110ca1a8680SCristian Marussi struct aidr {
111ca1a8680SCristian Marussi #define arch_minor_rev	GENMASK(3, 0)
112ca1a8680SCristian Marussi #define arch_major_rev	GENMASK(7, 4)
113ca1a8680SCristian Marussi 	u32 val;
114ca1a8680SCristian Marussi } __packed;
115ca1a8680SCristian Marussi 
116ca1a8680SCristian Marussi struct ctrl_page {
117ca1a8680SCristian Marussi 	struct blk_id blk_id;
118ca1a8680SCristian Marussi 	u8 pad[12];
119ca1a8680SCristian Marussi 	struct feat_spt0 feat_spt0;
120ca1a8680SCristian Marussi 	struct feat_spt1 feat_spt1;
121ca1a8680SCristian Marussi 	u8 pad1[8];
122ca1a8680SCristian Marussi 	struct dbch_cfg0 dbch_cfg0;
123ca1a8680SCristian Marussi 	u8 pad2[12];
124ca1a8680SCristian Marussi 	struct ffch_cfg0 ffch_cfg0;
125ca1a8680SCristian Marussi 	u8 pad3[12];
126ca1a8680SCristian Marussi 	struct fch_cfg0 fch_cfg0;
127ca1a8680SCristian Marussi 	u8 pad4[188];
128ca1a8680SCristian Marussi 	struct ctrl x_ctrl;
129ca1a8680SCristian Marussi 	/*-- MBX-only registers --*/
130ca1a8680SCristian Marussi 	u8 pad5[60];
131ca1a8680SCristian Marussi 	struct fch_ctrl fch_ctrl;
132ca1a8680SCristian Marussi 	u32 fcg_int_en;
133ca1a8680SCristian Marussi 	u8 pad6[696];
134ca1a8680SCristian Marussi 	/*-- End of MBX-only ---- */
135ca1a8680SCristian Marussi 	u32 dbch_int_st[MHUV3_DBCH_CMB_INT_ST_REG_CNT];
136ca1a8680SCristian Marussi 	u32 ffch_int_st[MHUV3_FFCH_CMB_INT_ST_REG_CNT];
137ca1a8680SCristian Marussi 	/*-- MBX-only registers --*/
138ca1a8680SCristian Marussi 	u8 pad7[88];
139ca1a8680SCristian Marussi 	u32 fcg_int_st;
140ca1a8680SCristian Marussi 	u8 pad8[12];
141ca1a8680SCristian Marussi 	u32 fcg_grp_int_st[32];
142ca1a8680SCristian Marussi 	u8 pad9[2760];
143ca1a8680SCristian Marussi 	/*-- End of MBX-only ---- */
144ca1a8680SCristian Marussi 	struct iidr iidr;
145ca1a8680SCristian Marussi 	struct aidr aidr;
146ca1a8680SCristian Marussi 	u32 imp_def_id[12];
147ca1a8680SCristian Marussi } __packed;
148ca1a8680SCristian Marussi 
149ca1a8680SCristian Marussi /* DBCW_Page */
150ca1a8680SCristian Marussi 
151ca1a8680SCristian Marussi struct xbcw_ctrl {
152ca1a8680SCristian Marussi #define comb_en		BIT(0)
153ca1a8680SCristian Marussi 	u32 val;
154ca1a8680SCristian Marussi } __packed;
155ca1a8680SCristian Marussi 
156ca1a8680SCristian Marussi struct pdbcw_int {
157ca1a8680SCristian Marussi #define tfr_ack		BIT(0)
158ca1a8680SCristian Marussi 	u32 val;
159ca1a8680SCristian Marussi } __packed;
160ca1a8680SCristian Marussi 
161ca1a8680SCristian Marussi struct pdbcw_page {
162ca1a8680SCristian Marussi 	u32 st;
163ca1a8680SCristian Marussi 	u8 pad[8];
164ca1a8680SCristian Marussi 	u32 set;
165ca1a8680SCristian Marussi 	struct pdbcw_int int_st;
166ca1a8680SCristian Marussi 	struct pdbcw_int int_clr;
167ca1a8680SCristian Marussi 	struct pdbcw_int int_en;
168ca1a8680SCristian Marussi 	struct xbcw_ctrl ctrl;
169ca1a8680SCristian Marussi } __packed;
170ca1a8680SCristian Marussi 
171ca1a8680SCristian Marussi struct mdbcw_page {
172ca1a8680SCristian Marussi 	u32 st;
173ca1a8680SCristian Marussi 	u32 st_msk;
174ca1a8680SCristian Marussi 	u32 clr;
175ca1a8680SCristian Marussi 	u8 pad[4];
176ca1a8680SCristian Marussi 	u32 msk_st;
177ca1a8680SCristian Marussi 	u32 msk_set;
178ca1a8680SCristian Marussi 	u32 msk_clr;
179ca1a8680SCristian Marussi 	struct xbcw_ctrl ctrl;
180ca1a8680SCristian Marussi } __packed;
181ca1a8680SCristian Marussi 
182ca1a8680SCristian Marussi struct dummy_page {
183ca1a8680SCristian Marussi 	u8 pad[SZ_4K];
184ca1a8680SCristian Marussi } __packed;
185ca1a8680SCristian Marussi 
186ca1a8680SCristian Marussi struct mhu3_pbx_frame_reg {
187ca1a8680SCristian Marussi 	struct ctrl_page ctrl;
188ca1a8680SCristian Marussi 	struct pdbcw_page dbcw[MHUV3_DBCW_MAX];
189ca1a8680SCristian Marussi 	struct dummy_page ffcw;
190ca1a8680SCristian Marussi 	struct dummy_page fcw;
191ca1a8680SCristian Marussi 	u8 pad[SZ_4K * 11];
192ca1a8680SCristian Marussi 	struct dummy_page impdef;
193ca1a8680SCristian Marussi } __packed;
194ca1a8680SCristian Marussi 
195ca1a8680SCristian Marussi struct mhu3_mbx_frame_reg {
196ca1a8680SCristian Marussi 	struct ctrl_page ctrl;
197ca1a8680SCristian Marussi 	struct mdbcw_page dbcw[MHUV3_DBCW_MAX];
198ca1a8680SCristian Marussi 	struct dummy_page ffcw;
199ca1a8680SCristian Marussi 	struct dummy_page fcw;
200ca1a8680SCristian Marussi 	u8 pad[SZ_4K * 11];
201ca1a8680SCristian Marussi 	struct dummy_page impdef;
202ca1a8680SCristian Marussi } __packed;
203ca1a8680SCristian Marussi 
204ca1a8680SCristian Marussi /* Macro for reading a bitmask within a physically mapped packed struct */
205ca1a8680SCristian Marussi #define readl_relaxed_bitmask(_regptr, _bitmask)			\
206ca1a8680SCristian Marussi 	({								\
207ca1a8680SCristian Marussi 		unsigned long _rval;					\
208ca1a8680SCristian Marussi 		_rval = readl_relaxed(_regptr);				\
209ca1a8680SCristian Marussi 		FIELD_GET(_bitmask, _rval);				\
210ca1a8680SCristian Marussi 	})
211ca1a8680SCristian Marussi 
212ca1a8680SCristian Marussi /* Macro for writing a bitmask within a physically mapped packed struct */
213ca1a8680SCristian Marussi #define writel_relaxed_bitmask(_value, _regptr, _bitmask)		\
214ca1a8680SCristian Marussi 	({								\
215ca1a8680SCristian Marussi 		unsigned long _rval;					\
216ca1a8680SCristian Marussi 		typeof(_regptr) _rptr = _regptr;			\
217ca1a8680SCristian Marussi 		typeof(_bitmask) _bmask = _bitmask;			\
218ca1a8680SCristian Marussi 		_rval = readl_relaxed(_rptr);				\
219ca1a8680SCristian Marussi 		_rval &= ~(_bmask);					\
220ca1a8680SCristian Marussi 		_rval |= FIELD_PREP((unsigned long long)_bmask, _value);\
221ca1a8680SCristian Marussi 		writel_relaxed(_rval, _rptr);				\
222ca1a8680SCristian Marussi 	})
223ca1a8680SCristian Marussi 
224ca1a8680SCristian Marussi /* ====== MHUv3 data structures ====== */
225ca1a8680SCristian Marussi 
226ca1a8680SCristian Marussi enum mhuv3_frame {
227ca1a8680SCristian Marussi 	PBX_FRAME,
228ca1a8680SCristian Marussi 	MBX_FRAME,
229ca1a8680SCristian Marussi };
230ca1a8680SCristian Marussi 
231ca1a8680SCristian Marussi static char *mhuv3_str[] = {
232ca1a8680SCristian Marussi 	"PBX",
233ca1a8680SCristian Marussi 	"MBX"
234ca1a8680SCristian Marussi };
235ca1a8680SCristian Marussi 
236ca1a8680SCristian Marussi enum mhuv3_extension_type {
237ca1a8680SCristian Marussi 	DBE_EXT,
238ca1a8680SCristian Marussi 	FCE_EXT,
239ca1a8680SCristian Marussi 	FE_EXT,
240ca1a8680SCristian Marussi 	NUM_EXT
241ca1a8680SCristian Marussi };
242ca1a8680SCristian Marussi 
243ca1a8680SCristian Marussi static char *mhuv3_ext_str[] = {
244ca1a8680SCristian Marussi 	"DBE",
245ca1a8680SCristian Marussi 	"FCE",
246ca1a8680SCristian Marussi 	"FE"
247ca1a8680SCristian Marussi };
248ca1a8680SCristian Marussi 
249ca1a8680SCristian Marussi struct mhuv3;
250ca1a8680SCristian Marussi 
251ca1a8680SCristian Marussi /**
252ca1a8680SCristian Marussi  * struct mhuv3_protocol_ops - MHUv3 operations
253ca1a8680SCristian Marussi  *
254ca1a8680SCristian Marussi  * @rx_startup: Receiver startup callback.
255ca1a8680SCristian Marussi  * @rx_shutdown: Receiver shutdown callback.
256ca1a8680SCristian Marussi  * @read_data: Read available Sender in-band LE data (if any).
257ca1a8680SCristian Marussi  * @rx_complete: Acknowledge data reception to the Sender. Any out-of-band data
258ca1a8680SCristian Marussi  *		 has to have been already retrieved before calling this.
259ca1a8680SCristian Marussi  * @tx_startup: Sender startup callback.
260ca1a8680SCristian Marussi  * @tx_shutdown: Sender shutdown callback.
261ca1a8680SCristian Marussi  * @last_tx_done: Report back to the Sender if the last transfer has completed.
262ca1a8680SCristian Marussi  * @send_data: Send data to the receiver.
263ca1a8680SCristian Marussi  *
264ca1a8680SCristian Marussi  * Each supported transport protocol provides its own implementation of
265ca1a8680SCristian Marussi  * these operations.
266ca1a8680SCristian Marussi  */
267ca1a8680SCristian Marussi struct mhuv3_protocol_ops {
268ca1a8680SCristian Marussi 	int (*rx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan);
269ca1a8680SCristian Marussi 	void (*rx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan);
270ca1a8680SCristian Marussi 	void *(*read_data)(struct mhuv3 *mhu, struct mbox_chan *chan);
271ca1a8680SCristian Marussi 	void (*rx_complete)(struct mhuv3 *mhu, struct mbox_chan *chan);
272ca1a8680SCristian Marussi 	void (*tx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan);
273ca1a8680SCristian Marussi 	void (*tx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan);
274ca1a8680SCristian Marussi 	int (*last_tx_done)(struct mhuv3 *mhu, struct mbox_chan *chan);
275ca1a8680SCristian Marussi 	int (*send_data)(struct mhuv3 *mhu, struct mbox_chan *chan, void *arg);
276ca1a8680SCristian Marussi };
277ca1a8680SCristian Marussi 
278ca1a8680SCristian Marussi /**
279ca1a8680SCristian Marussi  * struct mhuv3_mbox_chan_priv - MHUv3 channel private information
280ca1a8680SCristian Marussi  *
281ca1a8680SCristian Marussi  * @ch_idx: Channel window index associated to this mailbox channel.
282ca1a8680SCristian Marussi  * @doorbell: Doorbell bit number within the @ch_idx window.
283ca1a8680SCristian Marussi  *	      Only relevant to Doorbell transport.
284ca1a8680SCristian Marussi  * @ops: Transport protocol specific operations for this channel.
285ca1a8680SCristian Marussi  *
286ca1a8680SCristian Marussi  * Transport specific data attached to mmailbox channel priv data.
287ca1a8680SCristian Marussi  */
288ca1a8680SCristian Marussi struct mhuv3_mbox_chan_priv {
289ca1a8680SCristian Marussi 	u32 ch_idx;
290ca1a8680SCristian Marussi 	u32 doorbell;
291ca1a8680SCristian Marussi 	const struct mhuv3_protocol_ops *ops;
292ca1a8680SCristian Marussi };
293ca1a8680SCristian Marussi 
294ca1a8680SCristian Marussi /**
295ca1a8680SCristian Marussi  * struct mhuv3_extension - MHUv3 extension descriptor
296ca1a8680SCristian Marussi  *
297ca1a8680SCristian Marussi  * @type: Type of extension
298ca1a8680SCristian Marussi  * @num_chans: Max number of channels found for this extension.
299ca1a8680SCristian Marussi  * @base_ch_idx: First channel number assigned to this extension, picked from
300ca1a8680SCristian Marussi  *		 the set of all mailbox channels descriptors created.
301ca1a8680SCristian Marussi  * @mbox_of_xlate: Extension specific helper to parse DT and lookup associated
302ca1a8680SCristian Marussi  *		   channel from the related 'mboxes' property.
303ca1a8680SCristian Marussi  * @combined_irq_setup: Extension specific helper to setup the combined irq.
304ca1a8680SCristian Marussi  * @channels_init: Extension specific helper to initialize channels.
305ca1a8680SCristian Marussi  * @chan_from_comb_irq_get: Extension specific helper to lookup which channel
306ca1a8680SCristian Marussi  *			    triggered the combined irq.
307ca1a8680SCristian Marussi  * @pending_db: Array of per-channel pending doorbells.
308ca1a8680SCristian Marussi  * @pending_lock: Protect access to pending_db.
309ca1a8680SCristian Marussi  */
310ca1a8680SCristian Marussi struct mhuv3_extension {
311ca1a8680SCristian Marussi 	enum mhuv3_extension_type type;
312ca1a8680SCristian Marussi 	unsigned int num_chans;
313ca1a8680SCristian Marussi 	unsigned int base_ch_idx;
314ca1a8680SCristian Marussi 	struct mbox_chan *(*mbox_of_xlate)(struct mhuv3 *mhu,
315ca1a8680SCristian Marussi 					   unsigned int channel,
316ca1a8680SCristian Marussi 					   unsigned int param);
317ca1a8680SCristian Marussi 	void (*combined_irq_setup)(struct mhuv3 *mhu);
318ca1a8680SCristian Marussi 	int (*channels_init)(struct mhuv3 *mhu);
319ca1a8680SCristian Marussi 	struct mbox_chan *(*chan_from_comb_irq_get)(struct mhuv3 *mhu);
320ca1a8680SCristian Marussi 	u32 pending_db[MHUV3_DBCW_MAX];
321ca1a8680SCristian Marussi 	/* Protect access to pending_db */
322ca1a8680SCristian Marussi 	spinlock_t pending_lock;
323ca1a8680SCristian Marussi };
324ca1a8680SCristian Marussi 
325ca1a8680SCristian Marussi /**
326ca1a8680SCristian Marussi  * struct mhuv3 - MHUv3 mailbox controller data
327ca1a8680SCristian Marussi  *
328ca1a8680SCristian Marussi  * @frame:	Frame type: MBX_FRAME or PBX_FRAME.
329ca1a8680SCristian Marussi  * @auto_op_full: Flag to indicate if the MHU supports AutoOp full mode.
330ca1a8680SCristian Marussi  * @major: MHUv3 controller architectural major version.
331ca1a8680SCristian Marussi  * @minor: MHUv3 controller architectural minor version.
332ca1a8680SCristian Marussi  * @implem: MHUv3 controller IIDR implementer.
333ca1a8680SCristian Marussi  * @rev: MHUv3 controller IIDR revision.
334ca1a8680SCristian Marussi  * @var: MHUv3 controller IIDR variant.
335ca1a8680SCristian Marussi  * @prod_id: MHUv3 controller IIDR product_id.
336ca1a8680SCristian Marussi  * @num_chans: The total number of channnels discovered across all extensions.
337ca1a8680SCristian Marussi  * @cmb_irq: Combined IRQ number if any found defined.
338ca1a8680SCristian Marussi  * @ctrl: A reference to the MHUv3 control page for this block.
339ca1a8680SCristian Marussi  * @pbx: Base address of the PBX register mapping region.
340ca1a8680SCristian Marussi  * @mbx: Base address of the MBX register mapping region.
341ca1a8680SCristian Marussi  * @ext: Array holding descriptors for any found implemented extension.
342ca1a8680SCristian Marussi  * @mbox: Mailbox controller belonging to the MHU frame.
343ca1a8680SCristian Marussi  */
344ca1a8680SCristian Marussi struct mhuv3 {
345ca1a8680SCristian Marussi 	enum mhuv3_frame frame;
346ca1a8680SCristian Marussi 	bool auto_op_full;
347ca1a8680SCristian Marussi 	unsigned int major;
348ca1a8680SCristian Marussi 	unsigned int minor;
349ca1a8680SCristian Marussi 	unsigned int implem;
350ca1a8680SCristian Marussi 	unsigned int rev;
351ca1a8680SCristian Marussi 	unsigned int var;
352ca1a8680SCristian Marussi 	unsigned int prod_id;
353ca1a8680SCristian Marussi 	unsigned int num_chans;
354ca1a8680SCristian Marussi 	int cmb_irq;
355ca1a8680SCristian Marussi 	struct ctrl_page __iomem *ctrl;
356ca1a8680SCristian Marussi 	union {
357ca1a8680SCristian Marussi 		struct mhu3_pbx_frame_reg __iomem *pbx;
358ca1a8680SCristian Marussi 		struct mhu3_mbx_frame_reg __iomem *mbx;
359ca1a8680SCristian Marussi 	};
360ca1a8680SCristian Marussi 	struct mhuv3_extension *ext[NUM_EXT];
361ca1a8680SCristian Marussi 	struct mbox_controller mbox;
362ca1a8680SCristian Marussi };
363ca1a8680SCristian Marussi 
364ca1a8680SCristian Marussi #define mhu_from_mbox(_mbox) container_of(_mbox, struct mhuv3, mbox)
365ca1a8680SCristian Marussi 
366ca1a8680SCristian Marussi typedef int (*mhuv3_extension_initializer)(struct mhuv3 *mhu);
367ca1a8680SCristian Marussi 
368ca1a8680SCristian Marussi /* =================== Doorbell transport protocol operations =============== */
369ca1a8680SCristian Marussi 
mhuv3_doorbell_tx_startup(struct mhuv3 * mhu,struct mbox_chan * chan)370ca1a8680SCristian Marussi static void mhuv3_doorbell_tx_startup(struct mhuv3 *mhu, struct mbox_chan *chan)
371ca1a8680SCristian Marussi {
372ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
373ca1a8680SCristian Marussi 
374ca1a8680SCristian Marussi 	/* Enable Transfer Acknowledgment events */
375ca1a8680SCristian Marussi 	writel_relaxed_bitmask(0x1, &mhu->pbx->dbcw[priv->ch_idx].int_en, tfr_ack);
376ca1a8680SCristian Marussi }
377ca1a8680SCristian Marussi 
mhuv3_doorbell_tx_shutdown(struct mhuv3 * mhu,struct mbox_chan * chan)378ca1a8680SCristian Marussi static void mhuv3_doorbell_tx_shutdown(struct mhuv3 *mhu, struct mbox_chan *chan)
379ca1a8680SCristian Marussi {
380ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
381ca1a8680SCristian Marussi 	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
382ca1a8680SCristian Marussi 	unsigned long flags;
383ca1a8680SCristian Marussi 
384ca1a8680SCristian Marussi 	/* Disable Channel Transfer Ack events */
385ca1a8680SCristian Marussi 	writel_relaxed_bitmask(0x0, &mhu->pbx->dbcw[priv->ch_idx].int_en, tfr_ack);
386ca1a8680SCristian Marussi 
387ca1a8680SCristian Marussi 	/* Clear Channel Transfer Ack and pending doorbells */
388ca1a8680SCristian Marussi 	writel_relaxed_bitmask(0x1, &mhu->pbx->dbcw[priv->ch_idx].int_clr, tfr_ack);
389ca1a8680SCristian Marussi 	spin_lock_irqsave(&e->pending_lock, flags);
390ca1a8680SCristian Marussi 	e->pending_db[priv->ch_idx] = 0;
391ca1a8680SCristian Marussi 	spin_unlock_irqrestore(&e->pending_lock, flags);
392ca1a8680SCristian Marussi }
393ca1a8680SCristian Marussi 
mhuv3_doorbell_rx_startup(struct mhuv3 * mhu,struct mbox_chan * chan)394ca1a8680SCristian Marussi static int mhuv3_doorbell_rx_startup(struct mhuv3 *mhu, struct mbox_chan *chan)
395ca1a8680SCristian Marussi {
396ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
397ca1a8680SCristian Marussi 
398ca1a8680SCristian Marussi 	/* Unmask Channel Transfer events */
399ca1a8680SCristian Marussi 	writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].msk_clr);
400ca1a8680SCristian Marussi 
401ca1a8680SCristian Marussi 	return 0;
402ca1a8680SCristian Marussi }
403ca1a8680SCristian Marussi 
mhuv3_doorbell_rx_shutdown(struct mhuv3 * mhu,struct mbox_chan * chan)404ca1a8680SCristian Marussi static void mhuv3_doorbell_rx_shutdown(struct mhuv3 *mhu,
405ca1a8680SCristian Marussi 				       struct mbox_chan *chan)
406ca1a8680SCristian Marussi {
407ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
408ca1a8680SCristian Marussi 
409ca1a8680SCristian Marussi 	/* Mask Channel Transfer events */
410ca1a8680SCristian Marussi 	writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].msk_set);
411ca1a8680SCristian Marussi }
412ca1a8680SCristian Marussi 
mhuv3_doorbell_rx_complete(struct mhuv3 * mhu,struct mbox_chan * chan)413ca1a8680SCristian Marussi static void mhuv3_doorbell_rx_complete(struct mhuv3 *mhu, struct mbox_chan *chan)
414ca1a8680SCristian Marussi {
415ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
416ca1a8680SCristian Marussi 
417ca1a8680SCristian Marussi 	/* Clearing the pending transfer generates the Channel Transfer Ack */
418ca1a8680SCristian Marussi 	writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].clr);
419ca1a8680SCristian Marussi }
420ca1a8680SCristian Marussi 
mhuv3_doorbell_last_tx_done(struct mhuv3 * mhu,struct mbox_chan * chan)421ca1a8680SCristian Marussi static int mhuv3_doorbell_last_tx_done(struct mhuv3 *mhu,
422ca1a8680SCristian Marussi 				       struct mbox_chan *chan)
423ca1a8680SCristian Marussi {
424ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
425ca1a8680SCristian Marussi 	int done;
426ca1a8680SCristian Marussi 
427ca1a8680SCristian Marussi 	done = !(readl_relaxed(&mhu->pbx->dbcw[priv->ch_idx].st) &
428ca1a8680SCristian Marussi 		 BIT(priv->doorbell));
429ca1a8680SCristian Marussi 	if (done) {
430ca1a8680SCristian Marussi 		struct mhuv3_extension *e = mhu->ext[DBE_EXT];
431ca1a8680SCristian Marussi 		unsigned long flags;
432ca1a8680SCristian Marussi 
433ca1a8680SCristian Marussi 		/* Take care to clear the pending doorbell also when polling */
434ca1a8680SCristian Marussi 		spin_lock_irqsave(&e->pending_lock, flags);
435ca1a8680SCristian Marussi 		e->pending_db[priv->ch_idx] &= ~BIT(priv->doorbell);
436ca1a8680SCristian Marussi 		spin_unlock_irqrestore(&e->pending_lock, flags);
437ca1a8680SCristian Marussi 	}
438ca1a8680SCristian Marussi 
439ca1a8680SCristian Marussi 	return done;
440ca1a8680SCristian Marussi }
441ca1a8680SCristian Marussi 
mhuv3_doorbell_send_data(struct mhuv3 * mhu,struct mbox_chan * chan,void * arg)442ca1a8680SCristian Marussi static int mhuv3_doorbell_send_data(struct mhuv3 *mhu, struct mbox_chan *chan,
443ca1a8680SCristian Marussi 				    void *arg)
444ca1a8680SCristian Marussi {
445ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
446ca1a8680SCristian Marussi 	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
447ca1a8680SCristian Marussi 
448ca1a8680SCristian Marussi 	scoped_guard(spinlock_irqsave, &e->pending_lock) {
449ca1a8680SCristian Marussi 		/* Only one in-flight Transfer is allowed per-doorbell */
450ca1a8680SCristian Marussi 		if (e->pending_db[priv->ch_idx] & BIT(priv->doorbell))
451ca1a8680SCristian Marussi 			return -EBUSY;
452ca1a8680SCristian Marussi 
453ca1a8680SCristian Marussi 		e->pending_db[priv->ch_idx] |= BIT(priv->doorbell);
454ca1a8680SCristian Marussi 	}
455ca1a8680SCristian Marussi 
456ca1a8680SCristian Marussi 	writel_relaxed(BIT(priv->doorbell), &mhu->pbx->dbcw[priv->ch_idx].set);
457ca1a8680SCristian Marussi 
458ca1a8680SCristian Marussi 	return 0;
459ca1a8680SCristian Marussi }
460ca1a8680SCristian Marussi 
461ca1a8680SCristian Marussi static const struct mhuv3_protocol_ops mhuv3_doorbell_ops = {
462ca1a8680SCristian Marussi 	.tx_startup = mhuv3_doorbell_tx_startup,
463ca1a8680SCristian Marussi 	.tx_shutdown = mhuv3_doorbell_tx_shutdown,
464ca1a8680SCristian Marussi 	.rx_startup = mhuv3_doorbell_rx_startup,
465ca1a8680SCristian Marussi 	.rx_shutdown = mhuv3_doorbell_rx_shutdown,
466ca1a8680SCristian Marussi 	.rx_complete = mhuv3_doorbell_rx_complete,
467ca1a8680SCristian Marussi 	.last_tx_done = mhuv3_doorbell_last_tx_done,
468ca1a8680SCristian Marussi 	.send_data = mhuv3_doorbell_send_data,
469ca1a8680SCristian Marussi };
470ca1a8680SCristian Marussi 
471ca1a8680SCristian Marussi /* Sender and receiver mailbox ops */
mhuv3_sender_last_tx_done(struct mbox_chan * chan)472ca1a8680SCristian Marussi static bool mhuv3_sender_last_tx_done(struct mbox_chan *chan)
473ca1a8680SCristian Marussi {
474ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
475ca1a8680SCristian Marussi 	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
476ca1a8680SCristian Marussi 
477ca1a8680SCristian Marussi 	return priv->ops->last_tx_done(mhu, chan);
478ca1a8680SCristian Marussi }
479ca1a8680SCristian Marussi 
mhuv3_sender_send_data(struct mbox_chan * chan,void * data)480ca1a8680SCristian Marussi static int mhuv3_sender_send_data(struct mbox_chan *chan, void *data)
481ca1a8680SCristian Marussi {
482ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
483ca1a8680SCristian Marussi 	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
484ca1a8680SCristian Marussi 
485ca1a8680SCristian Marussi 	if (!priv->ops->last_tx_done(mhu, chan))
486ca1a8680SCristian Marussi 		return -EBUSY;
487ca1a8680SCristian Marussi 
488ca1a8680SCristian Marussi 	return priv->ops->send_data(mhu, chan, data);
489ca1a8680SCristian Marussi }
490ca1a8680SCristian Marussi 
mhuv3_sender_startup(struct mbox_chan * chan)491ca1a8680SCristian Marussi static int mhuv3_sender_startup(struct mbox_chan *chan)
492ca1a8680SCristian Marussi {
493ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
494ca1a8680SCristian Marussi 	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
495ca1a8680SCristian Marussi 
496ca1a8680SCristian Marussi 	if (priv->ops->tx_startup)
497ca1a8680SCristian Marussi 		priv->ops->tx_startup(mhu, chan);
498ca1a8680SCristian Marussi 
499ca1a8680SCristian Marussi 	return 0;
500ca1a8680SCristian Marussi }
501ca1a8680SCristian Marussi 
mhuv3_sender_shutdown(struct mbox_chan * chan)502ca1a8680SCristian Marussi static void mhuv3_sender_shutdown(struct mbox_chan *chan)
503ca1a8680SCristian Marussi {
504ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
505ca1a8680SCristian Marussi 	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
506ca1a8680SCristian Marussi 
507ca1a8680SCristian Marussi 	if (priv->ops->tx_shutdown)
508ca1a8680SCristian Marussi 		priv->ops->tx_shutdown(mhu, chan);
509ca1a8680SCristian Marussi }
510ca1a8680SCristian Marussi 
511ca1a8680SCristian Marussi static const struct mbox_chan_ops mhuv3_sender_ops = {
512ca1a8680SCristian Marussi 	.send_data = mhuv3_sender_send_data,
513ca1a8680SCristian Marussi 	.startup = mhuv3_sender_startup,
514ca1a8680SCristian Marussi 	.shutdown = mhuv3_sender_shutdown,
515ca1a8680SCristian Marussi 	.last_tx_done = mhuv3_sender_last_tx_done,
516ca1a8680SCristian Marussi };
517ca1a8680SCristian Marussi 
mhuv3_receiver_startup(struct mbox_chan * chan)518ca1a8680SCristian Marussi static int mhuv3_receiver_startup(struct mbox_chan *chan)
519ca1a8680SCristian Marussi {
520ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
521ca1a8680SCristian Marussi 	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
522ca1a8680SCristian Marussi 
523ca1a8680SCristian Marussi 	return priv->ops->rx_startup(mhu, chan);
524ca1a8680SCristian Marussi }
525ca1a8680SCristian Marussi 
mhuv3_receiver_shutdown(struct mbox_chan * chan)526ca1a8680SCristian Marussi static void mhuv3_receiver_shutdown(struct mbox_chan *chan)
527ca1a8680SCristian Marussi {
528ca1a8680SCristian Marussi 	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
529ca1a8680SCristian Marussi 	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
530ca1a8680SCristian Marussi 
531ca1a8680SCristian Marussi 	priv->ops->rx_shutdown(mhu, chan);
532ca1a8680SCristian Marussi }
533ca1a8680SCristian Marussi 
mhuv3_receiver_send_data(struct mbox_chan * chan,void * data)534ca1a8680SCristian Marussi static int mhuv3_receiver_send_data(struct mbox_chan *chan, void *data)
535ca1a8680SCristian Marussi {
536ca1a8680SCristian Marussi 	dev_err(chan->mbox->dev,
537ca1a8680SCristian Marussi 		"Trying to transmit on a MBX MHUv3 frame\n");
538ca1a8680SCristian Marussi 	return -EIO;
539ca1a8680SCristian Marussi }
540ca1a8680SCristian Marussi 
mhuv3_receiver_last_tx_done(struct mbox_chan * chan)541ca1a8680SCristian Marussi static bool mhuv3_receiver_last_tx_done(struct mbox_chan *chan)
542ca1a8680SCristian Marussi {
543ca1a8680SCristian Marussi 	dev_err(chan->mbox->dev, "Trying to Tx poll on a MBX MHUv3 frame\n");
544ca1a8680SCristian Marussi 	return true;
545ca1a8680SCristian Marussi }
546ca1a8680SCristian Marussi 
547ca1a8680SCristian Marussi static const struct mbox_chan_ops mhuv3_receiver_ops = {
548ca1a8680SCristian Marussi 	.send_data = mhuv3_receiver_send_data,
549ca1a8680SCristian Marussi 	.startup = mhuv3_receiver_startup,
550ca1a8680SCristian Marussi 	.shutdown = mhuv3_receiver_shutdown,
551ca1a8680SCristian Marussi 	.last_tx_done = mhuv3_receiver_last_tx_done,
552ca1a8680SCristian Marussi };
553ca1a8680SCristian Marussi 
mhuv3_dbe_mbox_of_xlate(struct mhuv3 * mhu,unsigned int channel,unsigned int doorbell)554ca1a8680SCristian Marussi static struct mbox_chan *mhuv3_dbe_mbox_of_xlate(struct mhuv3 *mhu,
555ca1a8680SCristian Marussi 						 unsigned int channel,
556ca1a8680SCristian Marussi 						 unsigned int doorbell)
557ca1a8680SCristian Marussi {
558ca1a8680SCristian Marussi 	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
559ca1a8680SCristian Marussi 	struct mbox_controller *mbox = &mhu->mbox;
560ca1a8680SCristian Marussi 	struct mbox_chan *chans = mbox->chans;
561ca1a8680SCristian Marussi 
562ca1a8680SCristian Marussi 	if (channel >= e->num_chans || doorbell >= MHUV3_FLAG_BITS) {
563ca1a8680SCristian Marussi 		dev_err(mbox->dev, "Couldn't xlate to a valid channel (%d: %d)\n",
564ca1a8680SCristian Marussi 			channel, doorbell);
565ca1a8680SCristian Marussi 		return ERR_PTR(-ENODEV);
566ca1a8680SCristian Marussi 	}
567ca1a8680SCristian Marussi 
568ca1a8680SCristian Marussi 	return &chans[e->base_ch_idx + channel * MHUV3_FLAG_BITS + doorbell];
569ca1a8680SCristian Marussi }
570ca1a8680SCristian Marussi 
mhuv3_dbe_combined_irq_setup(struct mhuv3 * mhu)571ca1a8680SCristian Marussi static void mhuv3_dbe_combined_irq_setup(struct mhuv3 *mhu)
572ca1a8680SCristian Marussi {
573ca1a8680SCristian Marussi 	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
574ca1a8680SCristian Marussi 	int i;
575ca1a8680SCristian Marussi 
576ca1a8680SCristian Marussi 	if (mhu->frame == PBX_FRAME) {
577ca1a8680SCristian Marussi 		struct pdbcw_page __iomem *dbcw = mhu->pbx->dbcw;
578ca1a8680SCristian Marussi 
579ca1a8680SCristian Marussi 		for (i = 0; i < e->num_chans; i++) {
580ca1a8680SCristian Marussi 			writel_relaxed_bitmask(0x1, &dbcw[i].int_clr, tfr_ack);
581ca1a8680SCristian Marussi 			writel_relaxed_bitmask(0x0, &dbcw[i].int_en, tfr_ack);
582ca1a8680SCristian Marussi 			writel_relaxed_bitmask(0x1, &dbcw[i].ctrl, comb_en);
583ca1a8680SCristian Marussi 		}
584ca1a8680SCristian Marussi 	} else {
585ca1a8680SCristian Marussi 		struct mdbcw_page __iomem *dbcw = mhu->mbx->dbcw;
586ca1a8680SCristian Marussi 
587ca1a8680SCristian Marussi 		for (i = 0; i < e->num_chans; i++) {
588ca1a8680SCristian Marussi 			writel_relaxed(0xFFFFFFFF, &dbcw[i].clr);
589ca1a8680SCristian Marussi 			writel_relaxed(0xFFFFFFFF, &dbcw[i].msk_set);
590ca1a8680SCristian Marussi 			writel_relaxed_bitmask(0x1, &dbcw[i].ctrl, comb_en);
591ca1a8680SCristian Marussi 		}
592ca1a8680SCristian Marussi 	}
593ca1a8680SCristian Marussi }
594ca1a8680SCristian Marussi 
mhuv3_dbe_channels_init(struct mhuv3 * mhu)595ca1a8680SCristian Marussi static int mhuv3_dbe_channels_init(struct mhuv3 *mhu)
596ca1a8680SCristian Marussi {
597ca1a8680SCristian Marussi 	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
598ca1a8680SCristian Marussi 	struct mbox_controller *mbox = &mhu->mbox;
599ca1a8680SCristian Marussi 	struct mbox_chan *chans;
600ca1a8680SCristian Marussi 	int i;
601ca1a8680SCristian Marussi 
602ca1a8680SCristian Marussi 	chans = mbox->chans + mbox->num_chans;
603ca1a8680SCristian Marussi 	e->base_ch_idx = mbox->num_chans;
604ca1a8680SCristian Marussi 	for (i = 0; i < e->num_chans; i++) {
605ca1a8680SCristian Marussi 		struct mhuv3_mbox_chan_priv *priv;
606ca1a8680SCristian Marussi 		int k;
607ca1a8680SCristian Marussi 
608ca1a8680SCristian Marussi 		for (k = 0; k < MHUV3_FLAG_BITS; k++) {
609ca1a8680SCristian Marussi 			priv = devm_kmalloc(mbox->dev, sizeof(*priv), GFP_KERNEL);
610ca1a8680SCristian Marussi 			if (!priv)
611ca1a8680SCristian Marussi 				return -ENOMEM;
612ca1a8680SCristian Marussi 
613ca1a8680SCristian Marussi 			priv->ch_idx = i;
614ca1a8680SCristian Marussi 			priv->ops = &mhuv3_doorbell_ops;
615ca1a8680SCristian Marussi 			priv->doorbell = k;
616ca1a8680SCristian Marussi 			chans++->con_priv = priv;
617ca1a8680SCristian Marussi 			mbox->num_chans++;
618ca1a8680SCristian Marussi 		}
619ca1a8680SCristian Marussi 	}
620ca1a8680SCristian Marussi 
621ca1a8680SCristian Marussi 	spin_lock_init(&e->pending_lock);
622ca1a8680SCristian Marussi 
623ca1a8680SCristian Marussi 	return 0;
624ca1a8680SCristian Marussi }
625ca1a8680SCristian Marussi 
mhuv3_dbe_doorbell_lookup(struct mhuv3 * mhu,unsigned int channel,unsigned int * db)626ca1a8680SCristian Marussi static bool mhuv3_dbe_doorbell_lookup(struct mhuv3 *mhu, unsigned int channel,
627ca1a8680SCristian Marussi 				      unsigned int *db)
628ca1a8680SCristian Marussi {
629ca1a8680SCristian Marussi 	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
630ca1a8680SCristian Marussi 	struct device *dev = mhu->mbox.dev;
631ca1a8680SCristian Marussi 	u32 st;
632ca1a8680SCristian Marussi 
633ca1a8680SCristian Marussi 	if (mhu->frame == PBX_FRAME) {
634ca1a8680SCristian Marussi 		u32 active_dbs, fired_dbs;
635ca1a8680SCristian Marussi 
636ca1a8680SCristian Marussi 		st = readl_relaxed_bitmask(&mhu->pbx->dbcw[channel].int_st,
637ca1a8680SCristian Marussi 					   tfr_ack);
638ca1a8680SCristian Marussi 		if (!st)
639ca1a8680SCristian Marussi 			goto err_spurious;
640ca1a8680SCristian Marussi 
641ca1a8680SCristian Marussi 		active_dbs = readl_relaxed(&mhu->pbx->dbcw[channel].st);
642ca1a8680SCristian Marussi 		scoped_guard(spinlock_irqsave, &e->pending_lock) {
643ca1a8680SCristian Marussi 			fired_dbs = e->pending_db[channel] & ~active_dbs;
644ca1a8680SCristian Marussi 			if (!fired_dbs)
645ca1a8680SCristian Marussi 				goto err_spurious;
646ca1a8680SCristian Marussi 
647ca1a8680SCristian Marussi 			*db = __ffs(fired_dbs);
648ca1a8680SCristian Marussi 			e->pending_db[channel] &= ~BIT(*db);
649ca1a8680SCristian Marussi 		}
650ca1a8680SCristian Marussi 		fired_dbs &= ~BIT(*db);
651ca1a8680SCristian Marussi 		/* Clear TFR Ack if no more doorbells pending */
652ca1a8680SCristian Marussi 		if (!fired_dbs)
653ca1a8680SCristian Marussi 			writel_relaxed_bitmask(0x1,
654ca1a8680SCristian Marussi 					       &mhu->pbx->dbcw[channel].int_clr,
655ca1a8680SCristian Marussi 					       tfr_ack);
656ca1a8680SCristian Marussi 	} else {
657ca1a8680SCristian Marussi 		st = readl_relaxed(&mhu->mbx->dbcw[channel].st_msk);
658ca1a8680SCristian Marussi 		if (!st)
659ca1a8680SCristian Marussi 			goto err_spurious;
660ca1a8680SCristian Marussi 
661ca1a8680SCristian Marussi 		*db = __ffs(st);
662ca1a8680SCristian Marussi 	}
663ca1a8680SCristian Marussi 
664ca1a8680SCristian Marussi 	return true;
665ca1a8680SCristian Marussi 
666ca1a8680SCristian Marussi err_spurious:
667ca1a8680SCristian Marussi 	dev_warn(dev, "Spurious IRQ on %s channel:%d\n",
668ca1a8680SCristian Marussi 		 mhuv3_str[mhu->frame], channel);
669ca1a8680SCristian Marussi 
670ca1a8680SCristian Marussi 	return false;
671ca1a8680SCristian Marussi }
672ca1a8680SCristian Marussi 
mhuv3_dbe_chan_from_comb_irq_get(struct mhuv3 * mhu)673ca1a8680SCristian Marussi static struct mbox_chan *mhuv3_dbe_chan_from_comb_irq_get(struct mhuv3 *mhu)
674ca1a8680SCristian Marussi {
675ca1a8680SCristian Marussi 	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
676ca1a8680SCristian Marussi 	struct device *dev = mhu->mbox.dev;
677ca1a8680SCristian Marussi 	int i;
678ca1a8680SCristian Marussi 
679ca1a8680SCristian Marussi 	for (i = 0; i < MHUV3_DBCH_CMB_INT_ST_REG_CNT; i++) {
680ca1a8680SCristian Marussi 		unsigned int channel, db;
681ca1a8680SCristian Marussi 		u32 cmb_st;
682ca1a8680SCristian Marussi 
683ca1a8680SCristian Marussi 		cmb_st = readl_relaxed(&mhu->ctrl->dbch_int_st[i]);
684ca1a8680SCristian Marussi 		if (!cmb_st)
685ca1a8680SCristian Marussi 			continue;
686ca1a8680SCristian Marussi 
687ca1a8680SCristian Marussi 		channel = i * MHUV3_FLAG_BITS + __ffs(cmb_st);
688ca1a8680SCristian Marussi 		if (channel >= e->num_chans) {
689ca1a8680SCristian Marussi 			dev_err(dev, "Invalid %s channel:%d\n",
690ca1a8680SCristian Marussi 				mhuv3_str[mhu->frame], channel);
691ca1a8680SCristian Marussi 			return ERR_PTR(-EIO);
692ca1a8680SCristian Marussi 		}
693ca1a8680SCristian Marussi 
694ca1a8680SCristian Marussi 		if (!mhuv3_dbe_doorbell_lookup(mhu, channel, &db))
695ca1a8680SCristian Marussi 			continue;
696ca1a8680SCristian Marussi 
697ca1a8680SCristian Marussi 		dev_dbg(dev, "Found %s ch[%d]/db[%d]\n",
698ca1a8680SCristian Marussi 			mhuv3_str[mhu->frame], channel, db);
699ca1a8680SCristian Marussi 
700ca1a8680SCristian Marussi 		return &mhu->mbox.chans[channel * MHUV3_FLAG_BITS + db];
701ca1a8680SCristian Marussi 	}
702ca1a8680SCristian Marussi 
703ca1a8680SCristian Marussi 	return ERR_PTR(-EIO);
704ca1a8680SCristian Marussi }
705ca1a8680SCristian Marussi 
mhuv3_dbe_init(struct mhuv3 * mhu)706ca1a8680SCristian Marussi static int mhuv3_dbe_init(struct mhuv3 *mhu)
707ca1a8680SCristian Marussi {
708ca1a8680SCristian Marussi 	struct device *dev = mhu->mbox.dev;
709ca1a8680SCristian Marussi 	struct mhuv3_extension *e;
710ca1a8680SCristian Marussi 
711ca1a8680SCristian Marussi 	if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, dbe_spt))
712ca1a8680SCristian Marussi 		return 0;
713ca1a8680SCristian Marussi 
714ca1a8680SCristian Marussi 	dev_dbg(dev, "%s: Initializing DBE Extension.\n", mhuv3_str[mhu->frame]);
715ca1a8680SCristian Marussi 
716ca1a8680SCristian Marussi 	e = devm_kzalloc(dev, sizeof(*e), GFP_KERNEL);
717ca1a8680SCristian Marussi 	if (!e)
718ca1a8680SCristian Marussi 		return -ENOMEM;
719ca1a8680SCristian Marussi 
720ca1a8680SCristian Marussi 	e->type = DBE_EXT;
721ca1a8680SCristian Marussi 	/* Note that, by the spec, the number of channels is (num_dbch + 1) */
722ca1a8680SCristian Marussi 	e->num_chans =
723ca1a8680SCristian Marussi 		readl_relaxed_bitmask(&mhu->ctrl->dbch_cfg0, num_dbch) + 1;
724ca1a8680SCristian Marussi 	e->mbox_of_xlate = mhuv3_dbe_mbox_of_xlate;
725ca1a8680SCristian Marussi 	e->combined_irq_setup = mhuv3_dbe_combined_irq_setup;
726ca1a8680SCristian Marussi 	e->channels_init = mhuv3_dbe_channels_init;
727ca1a8680SCristian Marussi 	e->chan_from_comb_irq_get = mhuv3_dbe_chan_from_comb_irq_get;
728ca1a8680SCristian Marussi 
729ca1a8680SCristian Marussi 	mhu->num_chans += e->num_chans * MHUV3_FLAG_BITS;
730ca1a8680SCristian Marussi 	mhu->ext[DBE_EXT] = e;
731ca1a8680SCristian Marussi 
732ca1a8680SCristian Marussi 	dev_dbg(dev, "%s: found %d DBE channels.\n",
733ca1a8680SCristian Marussi 		mhuv3_str[mhu->frame], e->num_chans);
734ca1a8680SCristian Marussi 
735ca1a8680SCristian Marussi 	return 0;
736ca1a8680SCristian Marussi }
737ca1a8680SCristian Marussi 
mhuv3_fce_init(struct mhuv3 * mhu)738ca1a8680SCristian Marussi static int mhuv3_fce_init(struct mhuv3 *mhu)
739ca1a8680SCristian Marussi {
740ca1a8680SCristian Marussi 	struct device *dev = mhu->mbox.dev;
741ca1a8680SCristian Marussi 
742ca1a8680SCristian Marussi 	if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, fce_spt))
743ca1a8680SCristian Marussi 		return 0;
744ca1a8680SCristian Marussi 
745ca1a8680SCristian Marussi 	dev_dbg(dev, "%s: FCE Extension not supported by driver.\n",
746ca1a8680SCristian Marussi 		mhuv3_str[mhu->frame]);
747ca1a8680SCristian Marussi 
748ca1a8680SCristian Marussi 	return 0;
749ca1a8680SCristian Marussi }
750ca1a8680SCristian Marussi 
mhuv3_fe_init(struct mhuv3 * mhu)751ca1a8680SCristian Marussi static int mhuv3_fe_init(struct mhuv3 *mhu)
752ca1a8680SCristian Marussi {
753ca1a8680SCristian Marussi 	struct device *dev = mhu->mbox.dev;
754ca1a8680SCristian Marussi 
755ca1a8680SCristian Marussi 	if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, fe_spt))
756ca1a8680SCristian Marussi 		return 0;
757ca1a8680SCristian Marussi 
758ca1a8680SCristian Marussi 	dev_dbg(dev, "%s: FE Extension not supported by driver.\n",
759ca1a8680SCristian Marussi 		mhuv3_str[mhu->frame]);
760ca1a8680SCristian Marussi 
761ca1a8680SCristian Marussi 	return 0;
762ca1a8680SCristian Marussi }
763ca1a8680SCristian Marussi 
764ca1a8680SCristian Marussi static mhuv3_extension_initializer mhuv3_extension_init[NUM_EXT] = {
765ca1a8680SCristian Marussi 	mhuv3_dbe_init,
766ca1a8680SCristian Marussi 	mhuv3_fce_init,
767ca1a8680SCristian Marussi 	mhuv3_fe_init,
768ca1a8680SCristian Marussi };
769ca1a8680SCristian Marussi 
mhuv3_initialize_channels(struct device * dev,struct mhuv3 * mhu)770ca1a8680SCristian Marussi static int mhuv3_initialize_channels(struct device *dev, struct mhuv3 *mhu)
771ca1a8680SCristian Marussi {
772ca1a8680SCristian Marussi 	struct mbox_controller *mbox = &mhu->mbox;
773ca1a8680SCristian Marussi 	int i, ret = 0;
774ca1a8680SCristian Marussi 
775ca1a8680SCristian Marussi 	mbox->chans = devm_kcalloc(dev, mhu->num_chans,
776ca1a8680SCristian Marussi 				   sizeof(*mbox->chans), GFP_KERNEL);
777ca1a8680SCristian Marussi 	if (!mbox->chans)
778ca1a8680SCristian Marussi 		return dev_err_probe(dev, -ENOMEM,
779ca1a8680SCristian Marussi 				     "Failed to initialize channels\n");
780ca1a8680SCristian Marussi 
781ca1a8680SCristian Marussi 	for (i = 0; i < NUM_EXT && !ret; i++)
782ca1a8680SCristian Marussi 		if (mhu->ext[i])
783ca1a8680SCristian Marussi 			ret = mhu->ext[i]->channels_init(mhu);
784ca1a8680SCristian Marussi 
785ca1a8680SCristian Marussi 	return ret;
786ca1a8680SCristian Marussi }
787ca1a8680SCristian Marussi 
mhuv3_mbox_of_xlate(struct mbox_controller * mbox,const struct of_phandle_args * pa)788ca1a8680SCristian Marussi static struct mbox_chan *mhuv3_mbox_of_xlate(struct mbox_controller *mbox,
789ca1a8680SCristian Marussi 					     const struct of_phandle_args *pa)
790ca1a8680SCristian Marussi {
791ca1a8680SCristian Marussi 	struct mhuv3 *mhu = mhu_from_mbox(mbox);
792ca1a8680SCristian Marussi 	unsigned int type, channel, param;
793ca1a8680SCristian Marussi 
794ca1a8680SCristian Marussi 	if (pa->args_count != MHUV3_MBOX_CELLS)
795ca1a8680SCristian Marussi 		return ERR_PTR(-EINVAL);
796ca1a8680SCristian Marussi 
797ca1a8680SCristian Marussi 	type = pa->args[MHUV3_MBOX_CELL_TYPE];
798ca1a8680SCristian Marussi 	if (type >= NUM_EXT)
799ca1a8680SCristian Marussi 		return ERR_PTR(-EINVAL);
800ca1a8680SCristian Marussi 
801ca1a8680SCristian Marussi 	channel = pa->args[MHUV3_MBOX_CELL_CHWN];
802ca1a8680SCristian Marussi 	param = pa->args[MHUV3_MBOX_CELL_PARAM];
803ca1a8680SCristian Marussi 
804ca1a8680SCristian Marussi 	return mhu->ext[type]->mbox_of_xlate(mhu, channel, param);
805ca1a8680SCristian Marussi }
806ca1a8680SCristian Marussi 
mhu_frame_cleanup_actions(void * data)807ca1a8680SCristian Marussi static void mhu_frame_cleanup_actions(void *data)
808ca1a8680SCristian Marussi {
809ca1a8680SCristian Marussi 	struct mhuv3 *mhu = data;
810ca1a8680SCristian Marussi 
811ca1a8680SCristian Marussi 	writel_relaxed_bitmask(0x0, &mhu->ctrl->x_ctrl, op_req);
812ca1a8680SCristian Marussi }
813ca1a8680SCristian Marussi 
mhuv3_frame_init(struct mhuv3 * mhu,void __iomem * regs)814ca1a8680SCristian Marussi static int mhuv3_frame_init(struct mhuv3 *mhu, void __iomem *regs)
815ca1a8680SCristian Marussi {
816ca1a8680SCristian Marussi 	struct device *dev = mhu->mbox.dev;
817ca1a8680SCristian Marussi 	int i;
818ca1a8680SCristian Marussi 
819ca1a8680SCristian Marussi 	mhu->ctrl = regs;
820ca1a8680SCristian Marussi 	mhu->frame = readl_relaxed_bitmask(&mhu->ctrl->blk_id, id);
821ca1a8680SCristian Marussi 	if (mhu->frame > MBX_FRAME)
822ca1a8680SCristian Marussi 		return dev_err_probe(dev, -EINVAL,
823ca1a8680SCristian Marussi 				     "Invalid Frame type- %d\n", mhu->frame);
824ca1a8680SCristian Marussi 
825ca1a8680SCristian Marussi 	mhu->major = readl_relaxed_bitmask(&mhu->ctrl->aidr, arch_major_rev);
826ca1a8680SCristian Marussi 	mhu->minor = readl_relaxed_bitmask(&mhu->ctrl->aidr, arch_minor_rev);
827ca1a8680SCristian Marussi 	mhu->implem = readl_relaxed_bitmask(&mhu->ctrl->iidr, implementer);
828ca1a8680SCristian Marussi 	mhu->rev = readl_relaxed_bitmask(&mhu->ctrl->iidr, revision);
829ca1a8680SCristian Marussi 	mhu->var = readl_relaxed_bitmask(&mhu->ctrl->iidr, variant);
830ca1a8680SCristian Marussi 	mhu->prod_id = readl_relaxed_bitmask(&mhu->ctrl->iidr, product_id);
831ca1a8680SCristian Marussi 	if (mhu->major != MHUV3_MAJOR_VERSION)
832ca1a8680SCristian Marussi 		return dev_err_probe(dev, -EINVAL,
833ca1a8680SCristian Marussi 				     "Unsupported MHU %s block - major:%d  minor:%d\n",
834ca1a8680SCristian Marussi 				     mhuv3_str[mhu->frame], mhu->major,
835ca1a8680SCristian Marussi 				     mhu->minor);
836ca1a8680SCristian Marussi 
837ca1a8680SCristian Marussi 	mhu->auto_op_full =
838ca1a8680SCristian Marussi 		!!readl_relaxed_bitmask(&mhu->ctrl->feat_spt1, auto_op_spt);
839ca1a8680SCristian Marussi 	/* Request the PBX/MBX to remain operational */
840ca1a8680SCristian Marussi 	if (mhu->auto_op_full) {
841ca1a8680SCristian Marussi 		writel_relaxed_bitmask(0x1, &mhu->ctrl->x_ctrl, op_req);
842ca1a8680SCristian Marussi 		devm_add_action_or_reset(dev, mhu_frame_cleanup_actions, mhu);
843ca1a8680SCristian Marussi 	}
844ca1a8680SCristian Marussi 
845ca1a8680SCristian Marussi 	dev_dbg(dev,
846ca1a8680SCristian Marussi 		"Found MHU %s block - major:%d  minor:%d\n  implem:0x%X  rev:0x%X  var:0x%X  prod_id:0x%X",
847ca1a8680SCristian Marussi 		mhuv3_str[mhu->frame], mhu->major, mhu->minor,
848ca1a8680SCristian Marussi 		mhu->implem, mhu->rev, mhu->var, mhu->prod_id);
849ca1a8680SCristian Marussi 
850ca1a8680SCristian Marussi 	if (mhu->frame == PBX_FRAME)
851ca1a8680SCristian Marussi 		mhu->pbx = regs;
852ca1a8680SCristian Marussi 	else
853ca1a8680SCristian Marussi 		mhu->mbx = regs;
854ca1a8680SCristian Marussi 
855ca1a8680SCristian Marussi 	for (i = 0; i < NUM_EXT; i++) {
856ca1a8680SCristian Marussi 		int ret;
857ca1a8680SCristian Marussi 
858ca1a8680SCristian Marussi 		/*
859ca1a8680SCristian Marussi 		 * Note that extensions initialization fails only when such
860ca1a8680SCristian Marussi 		 * extension initialization routine fails and the extensions
861ca1a8680SCristian Marussi 		 * was found to be supported in hardware and in software.
862ca1a8680SCristian Marussi 		 */
863ca1a8680SCristian Marussi 		ret = mhuv3_extension_init[i](mhu);
864ca1a8680SCristian Marussi 		if (ret)
865ca1a8680SCristian Marussi 			return dev_err_probe(dev, ret,
866ca1a8680SCristian Marussi 					     "Failed to initialize %s %s\n",
867ca1a8680SCristian Marussi 					     mhuv3_str[mhu->frame],
868ca1a8680SCristian Marussi 					     mhuv3_ext_str[i]);
869ca1a8680SCristian Marussi 	}
870ca1a8680SCristian Marussi 
871ca1a8680SCristian Marussi 	return 0;
872ca1a8680SCristian Marussi }
873ca1a8680SCristian Marussi 
mhuv3_pbx_comb_interrupt(int irq,void * arg)874ca1a8680SCristian Marussi static irqreturn_t mhuv3_pbx_comb_interrupt(int irq, void *arg)
875ca1a8680SCristian Marussi {
876ca1a8680SCristian Marussi 	unsigned int i, found = 0;
877ca1a8680SCristian Marussi 	struct mhuv3 *mhu = arg;
878ca1a8680SCristian Marussi 	struct mbox_chan *chan;
879ca1a8680SCristian Marussi 	struct device *dev;
880ca1a8680SCristian Marussi 	int ret = IRQ_NONE;
881ca1a8680SCristian Marussi 
882ca1a8680SCristian Marussi 	dev = mhu->mbox.dev;
883ca1a8680SCristian Marussi 	for (i = 0; i < NUM_EXT; i++) {
884ca1a8680SCristian Marussi 		struct mhuv3_mbox_chan_priv *priv;
885ca1a8680SCristian Marussi 
886ca1a8680SCristian Marussi 		/* FCE does not participate to the PBX combined */
887ca1a8680SCristian Marussi 		if (i == FCE_EXT || !mhu->ext[i])
888ca1a8680SCristian Marussi 			continue;
889ca1a8680SCristian Marussi 
890ca1a8680SCristian Marussi 		chan = mhu->ext[i]->chan_from_comb_irq_get(mhu);
891ca1a8680SCristian Marussi 		if (IS_ERR(chan))
892ca1a8680SCristian Marussi 			continue;
893ca1a8680SCristian Marussi 
894ca1a8680SCristian Marussi 		found++;
895ca1a8680SCristian Marussi 		priv = chan->con_priv;
896ca1a8680SCristian Marussi 		if (!chan->cl) {
897ca1a8680SCristian Marussi 			dev_warn(dev, "TX Ack on UNBOUND channel (%u)\n",
898ca1a8680SCristian Marussi 				 priv->ch_idx);
899ca1a8680SCristian Marussi 			continue;
900ca1a8680SCristian Marussi 		}
901ca1a8680SCristian Marussi 
902ca1a8680SCristian Marussi 		mbox_chan_txdone(chan, 0);
903ca1a8680SCristian Marussi 		ret = IRQ_HANDLED;
904ca1a8680SCristian Marussi 	}
905ca1a8680SCristian Marussi 
906ca1a8680SCristian Marussi 	if (found == 0)
907ca1a8680SCristian Marussi 		dev_warn_once(dev, "Failed to find channel for the TX interrupt\n");
908ca1a8680SCristian Marussi 
909ca1a8680SCristian Marussi 	return ret;
910ca1a8680SCristian Marussi }
911ca1a8680SCristian Marussi 
mhuv3_mbx_comb_interrupt(int irq,void * arg)912ca1a8680SCristian Marussi static irqreturn_t mhuv3_mbx_comb_interrupt(int irq, void *arg)
913ca1a8680SCristian Marussi {
914ca1a8680SCristian Marussi 	unsigned int i, found = 0;
915ca1a8680SCristian Marussi 	struct mhuv3 *mhu = arg;
916ca1a8680SCristian Marussi 	struct mbox_chan *chan;
917ca1a8680SCristian Marussi 	struct device *dev;
918ca1a8680SCristian Marussi 	int ret = IRQ_NONE;
919ca1a8680SCristian Marussi 
920ca1a8680SCristian Marussi 	dev = mhu->mbox.dev;
921ca1a8680SCristian Marussi 	for (i = 0; i < NUM_EXT; i++) {
922ca1a8680SCristian Marussi 		struct mhuv3_mbox_chan_priv *priv;
923ca1a8680SCristian Marussi 		void *data __free(kfree) = NULL;
924ca1a8680SCristian Marussi 
925ca1a8680SCristian Marussi 		if (!mhu->ext[i])
926ca1a8680SCristian Marussi 			continue;
927ca1a8680SCristian Marussi 
928ca1a8680SCristian Marussi 		/* Process any extension which could be source of the IRQ */
929ca1a8680SCristian Marussi 		chan = mhu->ext[i]->chan_from_comb_irq_get(mhu);
930ca1a8680SCristian Marussi 		if (IS_ERR(chan))
931ca1a8680SCristian Marussi 			continue;
932ca1a8680SCristian Marussi 
933ca1a8680SCristian Marussi 		found++;
934ca1a8680SCristian Marussi 		/* From here on we need to call rx_complete even on error */
935ca1a8680SCristian Marussi 		priv = chan->con_priv;
936ca1a8680SCristian Marussi 		if (!chan->cl) {
937ca1a8680SCristian Marussi 			dev_warn(dev, "RX Data on UNBOUND channel (%u)\n",
938ca1a8680SCristian Marussi 				 priv->ch_idx);
939ca1a8680SCristian Marussi 			goto rx_ack;
940ca1a8680SCristian Marussi 		}
941ca1a8680SCristian Marussi 
942ca1a8680SCristian Marussi 		/* Read optional in-band LE data first. */
943ca1a8680SCristian Marussi 		if (priv->ops->read_data) {
944ca1a8680SCristian Marussi 			data = priv->ops->read_data(mhu, chan);
945ca1a8680SCristian Marussi 			if (IS_ERR(data)) {
946ca1a8680SCristian Marussi 				dev_err(dev,
947ca1a8680SCristian Marussi 					"Failed to read in-band data. err:%ld\n",
948ca1a8680SCristian Marussi 					PTR_ERR(no_free_ptr(data)));
949ca1a8680SCristian Marussi 				goto rx_ack;
950ca1a8680SCristian Marussi 			}
951ca1a8680SCristian Marussi 		}
952ca1a8680SCristian Marussi 
953ca1a8680SCristian Marussi 		mbox_chan_received_data(chan, data);
954ca1a8680SCristian Marussi 		ret = IRQ_HANDLED;
955ca1a8680SCristian Marussi 
956ca1a8680SCristian Marussi 		/*
957ca1a8680SCristian Marussi 		 * Acknowledge transfer after any possible optional
958ca1a8680SCristian Marussi 		 * out-of-band data has also been retrieved via
959ca1a8680SCristian Marussi 		 * mbox_chan_received_data().
960ca1a8680SCristian Marussi 		 */
961ca1a8680SCristian Marussi rx_ack:
962ca1a8680SCristian Marussi 		if (priv->ops->rx_complete)
963ca1a8680SCristian Marussi 			priv->ops->rx_complete(mhu, chan);
964ca1a8680SCristian Marussi 	}
965ca1a8680SCristian Marussi 
966ca1a8680SCristian Marussi 	if (found == 0)
967ca1a8680SCristian Marussi 		dev_warn_once(dev, "Failed to find channel for the RX interrupt\n");
968ca1a8680SCristian Marussi 
969ca1a8680SCristian Marussi 	return ret;
970ca1a8680SCristian Marussi }
971ca1a8680SCristian Marussi 
mhuv3_setup_pbx(struct mhuv3 * mhu)972ca1a8680SCristian Marussi static int mhuv3_setup_pbx(struct mhuv3 *mhu)
973ca1a8680SCristian Marussi {
974ca1a8680SCristian Marussi 	struct device *dev = mhu->mbox.dev;
975ca1a8680SCristian Marussi 
976ca1a8680SCristian Marussi 	mhu->mbox.ops = &mhuv3_sender_ops;
977ca1a8680SCristian Marussi 
978ca1a8680SCristian Marussi 	if (mhu->cmb_irq > 0) {
979ca1a8680SCristian Marussi 		int ret, i;
980ca1a8680SCristian Marussi 
981ca1a8680SCristian Marussi 		ret = devm_request_threaded_irq(dev, mhu->cmb_irq, NULL,
982ca1a8680SCristian Marussi 						mhuv3_pbx_comb_interrupt,
983ca1a8680SCristian Marussi 						IRQF_ONESHOT, "mhuv3-pbx", mhu);
984ca1a8680SCristian Marussi 		if (ret)
985ca1a8680SCristian Marussi 			return dev_err_probe(dev, ret,
986ca1a8680SCristian Marussi 					     "Failed to request PBX IRQ\n");
987ca1a8680SCristian Marussi 
988ca1a8680SCristian Marussi 		mhu->mbox.txdone_irq = true;
989ca1a8680SCristian Marussi 		mhu->mbox.txdone_poll = false;
990ca1a8680SCristian Marussi 
991ca1a8680SCristian Marussi 		for (i = 0; i < NUM_EXT; i++)
992ca1a8680SCristian Marussi 			if (mhu->ext[i])
993ca1a8680SCristian Marussi 				mhu->ext[i]->combined_irq_setup(mhu);
994ca1a8680SCristian Marussi 
995ca1a8680SCristian Marussi 		dev_dbg(dev, "MHUv3 PBX IRQs initialized.\n");
996ca1a8680SCristian Marussi 
997ca1a8680SCristian Marussi 		return 0;
998ca1a8680SCristian Marussi 	}
999ca1a8680SCristian Marussi 
1000ca1a8680SCristian Marussi 	dev_info(dev, "Using PBX in Tx polling mode.\n");
1001ca1a8680SCristian Marussi 	mhu->mbox.txdone_irq = false;
1002ca1a8680SCristian Marussi 	mhu->mbox.txdone_poll = true;
1003ca1a8680SCristian Marussi 	mhu->mbox.txpoll_period = 1;
1004ca1a8680SCristian Marussi 
1005ca1a8680SCristian Marussi 	return 0;
1006ca1a8680SCristian Marussi }
1007ca1a8680SCristian Marussi 
mhuv3_setup_mbx(struct mhuv3 * mhu)1008ca1a8680SCristian Marussi static int mhuv3_setup_mbx(struct mhuv3 *mhu)
1009ca1a8680SCristian Marussi {
1010ca1a8680SCristian Marussi 	struct device *dev = mhu->mbox.dev;
1011ca1a8680SCristian Marussi 	int ret, i;
1012ca1a8680SCristian Marussi 
1013ca1a8680SCristian Marussi 	mhu->mbox.ops = &mhuv3_receiver_ops;
1014ca1a8680SCristian Marussi 
1015ca1a8680SCristian Marussi 	if (mhu->cmb_irq <= 0)
1016ca1a8680SCristian Marussi 		return dev_err_probe(dev, -EINVAL,
1017ca1a8680SCristian Marussi 				     "MBX combined IRQ is missing !\n");
1018ca1a8680SCristian Marussi 
1019ca1a8680SCristian Marussi 	ret = devm_request_threaded_irq(dev, mhu->cmb_irq, NULL,
1020ca1a8680SCristian Marussi 					mhuv3_mbx_comb_interrupt, IRQF_ONESHOT,
1021ca1a8680SCristian Marussi 					"mhuv3-mbx", mhu);
1022ca1a8680SCristian Marussi 	if (ret)
1023ca1a8680SCristian Marussi 		return dev_err_probe(dev, ret, "Failed to request MBX IRQ\n");
1024ca1a8680SCristian Marussi 
1025ca1a8680SCristian Marussi 	for (i = 0; i < NUM_EXT; i++)
1026ca1a8680SCristian Marussi 		if (mhu->ext[i])
1027ca1a8680SCristian Marussi 			mhu->ext[i]->combined_irq_setup(mhu);
1028ca1a8680SCristian Marussi 
1029ca1a8680SCristian Marussi 	dev_dbg(dev, "MHUv3 MBX IRQs initialized.\n");
1030ca1a8680SCristian Marussi 
1031ca1a8680SCristian Marussi 	return ret;
1032ca1a8680SCristian Marussi }
1033ca1a8680SCristian Marussi 
mhuv3_irqs_init(struct mhuv3 * mhu,struct platform_device * pdev)1034ca1a8680SCristian Marussi static int mhuv3_irqs_init(struct mhuv3 *mhu, struct platform_device *pdev)
1035ca1a8680SCristian Marussi {
1036ca1a8680SCristian Marussi 	dev_dbg(mhu->mbox.dev, "Initializing %s block.\n",
1037ca1a8680SCristian Marussi 		mhuv3_str[mhu->frame]);
1038ca1a8680SCristian Marussi 
1039ca1a8680SCristian Marussi 	if (mhu->frame == PBX_FRAME) {
1040ca1a8680SCristian Marussi 		mhu->cmb_irq =
1041ca1a8680SCristian Marussi 			platform_get_irq_byname_optional(pdev, "combined");
1042ca1a8680SCristian Marussi 		return mhuv3_setup_pbx(mhu);
1043ca1a8680SCristian Marussi 	}
1044ca1a8680SCristian Marussi 
1045ca1a8680SCristian Marussi 	mhu->cmb_irq = platform_get_irq_byname(pdev, "combined");
1046ca1a8680SCristian Marussi 	return mhuv3_setup_mbx(mhu);
1047ca1a8680SCristian Marussi }
1048ca1a8680SCristian Marussi 
mhuv3_probe(struct platform_device * pdev)1049ca1a8680SCristian Marussi static int mhuv3_probe(struct platform_device *pdev)
1050ca1a8680SCristian Marussi {
1051ca1a8680SCristian Marussi 	struct device *dev = &pdev->dev;
1052ca1a8680SCristian Marussi 	void __iomem *regs;
1053ca1a8680SCristian Marussi 	struct mhuv3 *mhu;
1054ca1a8680SCristian Marussi 	int ret;
1055ca1a8680SCristian Marussi 
1056ca1a8680SCristian Marussi 	mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);
1057ca1a8680SCristian Marussi 	if (!mhu)
1058ca1a8680SCristian Marussi 		return -ENOMEM;
1059ca1a8680SCristian Marussi 
1060ca1a8680SCristian Marussi 	regs = devm_platform_ioremap_resource(pdev, 0);
1061ca1a8680SCristian Marussi 	if (IS_ERR(regs))
1062ca1a8680SCristian Marussi 		return PTR_ERR(regs);
1063ca1a8680SCristian Marussi 
1064ca1a8680SCristian Marussi 	mhu->mbox.dev = dev;
1065ca1a8680SCristian Marussi 	ret = mhuv3_frame_init(mhu, regs);
1066ca1a8680SCristian Marussi 	if (ret)
1067ca1a8680SCristian Marussi 		return ret;
1068ca1a8680SCristian Marussi 
1069ca1a8680SCristian Marussi 	ret = mhuv3_irqs_init(mhu, pdev);
1070ca1a8680SCristian Marussi 	if (ret)
1071ca1a8680SCristian Marussi 		return ret;
1072ca1a8680SCristian Marussi 
1073ca1a8680SCristian Marussi 	mhu->mbox.of_xlate = mhuv3_mbox_of_xlate;
1074ca1a8680SCristian Marussi 	ret = mhuv3_initialize_channels(dev, mhu);
1075ca1a8680SCristian Marussi 	if (ret)
1076ca1a8680SCristian Marussi 		return ret;
1077ca1a8680SCristian Marussi 
1078ca1a8680SCristian Marussi 	ret = devm_mbox_controller_register(dev, &mhu->mbox);
1079ca1a8680SCristian Marussi 	if (ret)
1080ca1a8680SCristian Marussi 		return dev_err_probe(dev, ret,
1081ca1a8680SCristian Marussi 				     "Failed to register ARM MHUv3 driver\n");
1082ca1a8680SCristian Marussi 
1083ca1a8680SCristian Marussi 	return ret;
1084ca1a8680SCristian Marussi }
1085ca1a8680SCristian Marussi 
1086ca1a8680SCristian Marussi static const struct of_device_id mhuv3_of_match[] = {
1087ca1a8680SCristian Marussi 	{ .compatible = "arm,mhuv3", .data = NULL },
1088ca1a8680SCristian Marussi 	{}
1089ca1a8680SCristian Marussi };
1090ca1a8680SCristian Marussi MODULE_DEVICE_TABLE(of, mhuv3_of_match);
1091ca1a8680SCristian Marussi 
1092ca1a8680SCristian Marussi static struct platform_driver mhuv3_driver = {
1093ca1a8680SCristian Marussi 	.driver = {
1094ca1a8680SCristian Marussi 		.name = "arm-mhuv3-mailbox",
1095ca1a8680SCristian Marussi 		.of_match_table = mhuv3_of_match,
1096ca1a8680SCristian Marussi 	},
1097ca1a8680SCristian Marussi 	.probe = mhuv3_probe,
1098ca1a8680SCristian Marussi };
1099ca1a8680SCristian Marussi module_platform_driver(mhuv3_driver);
1100ca1a8680SCristian Marussi 
1101ca1a8680SCristian Marussi MODULE_LICENSE("GPL");
1102ca1a8680SCristian Marussi MODULE_DESCRIPTION("ARM MHUv3 Driver");
1103ca1a8680SCristian Marussi MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
1104