Blame view

drivers/mailbox/hi3660-mailbox.c 7.44 KB
41c0e939d   Kaihua Zhong   mailbox: Add supp...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  // SPDX-License-Identifier: GPL-2.0
  // Copyright (c) 2017-2018 Hisilicon Limited.
  // Copyright (c) 2017-2018 Linaro Limited.
  
  #include <linux/bitops.h>
  #include <linux/delay.h>
  #include <linux/device.h>
  #include <linux/err.h>
  #include <linux/interrupt.h>
  #include <linux/io.h>
  #include <linux/iopoll.h>
  #include <linux/mailbox_controller.h>
  #include <linux/module.h>
  #include <linux/platform_device.h>
  #include <linux/slab.h>
  
  #include "mailbox.h"
  
  #define MBOX_CHAN_MAX			32
  
  #define MBOX_RX				0x0
  #define MBOX_TX				0x1
  
  #define MBOX_BASE(mbox, ch)		((mbox)->base + ((ch) * 0x40))
  #define MBOX_SRC_REG			0x00
  #define MBOX_DST_REG			0x04
  #define MBOX_DCLR_REG			0x08
  #define MBOX_DSTAT_REG			0x0c
  #define MBOX_MODE_REG			0x10
  #define MBOX_IMASK_REG			0x14
  #define MBOX_ICLR_REG			0x18
  #define MBOX_SEND_REG			0x1c
  #define MBOX_DATA_REG			0x20
  
  #define MBOX_IPC_LOCK_REG		0xa00
  #define MBOX_IPC_UNLOCK			0x1acce551
  
  #define MBOX_AUTOMATIC_ACK		1
  
  #define MBOX_STATE_IDLE			BIT(4)
2e4ac7cc4   Kevin Wangtao   mailbox: Hi3660: ...
41
  #define MBOX_STATE_READY		BIT(5)
41c0e939d   Kaihua Zhong   mailbox: Add supp...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  #define MBOX_STATE_ACK			BIT(7)
  
  #define MBOX_MSG_LEN			8
  
  /**
   * Hi3660 mailbox channel information
   *
   * A channel can be used for TX or RX, it can trigger remote
   * processor interrupt to notify remote processor and can receive
   * interrupt if has incoming message.
   *
   * @dst_irq:	Interrupt vector for remote processor
   * @ack_irq:	Interrupt vector for local processor
   */
  struct hi3660_chan_info {
  	unsigned int dst_irq;
  	unsigned int ack_irq;
  };
  
  /**
   * Hi3660 mailbox controller data
   *
   * Mailbox controller includes 32 channels and can allocate
   * channel for message transferring.
   *
   * @dev:	Device to which it is attached
   * @base:	Base address of the register mapping region
   * @chan:	Representation of channels in mailbox controller
   * @mchan:	Representation of channel info
   * @controller:	Representation of a communication channel controller
   */
  struct hi3660_mbox {
  	struct device *dev;
  	void __iomem *base;
  	struct mbox_chan chan[MBOX_CHAN_MAX];
  	struct hi3660_chan_info mchan[MBOX_CHAN_MAX];
  	struct mbox_controller controller;
  };
  
  static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox)
  {
  	return container_of(mbox, struct hi3660_mbox, controller);
  }
  
  static int hi3660_mbox_check_state(struct mbox_chan *chan)
  {
  	unsigned long ch = (unsigned long)chan->con_priv;
  	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
  	struct hi3660_chan_info *mchan = &mbox->mchan[ch];
  	void __iomem *base = MBOX_BASE(mbox, ch);
  	unsigned long val;
  	unsigned int ret;
2e4ac7cc4   Kevin Wangtao   mailbox: Hi3660: ...
94
95
  	/* Mailbox is ready to use */
  	if (readl(base + MBOX_MODE_REG) & MBOX_STATE_READY)
41c0e939d   Kaihua Zhong   mailbox: Add supp...
96
97
98
99
100
101
102
103
104
105
  		return 0;
  
  	/* Wait for acknowledge from remote */
  	ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG,
  			val, (val & MBOX_STATE_ACK), 1000, 300000);
  	if (ret) {
  		dev_err(mbox->dev, "%s: timeout for receiving ack
  ", __func__);
  		return ret;
  	}
2e4ac7cc4   Kevin Wangtao   mailbox: Hi3660: ...
106
107
  	/* clear ack state, mailbox will get back to ready state */
  	writel(BIT(mchan->ack_irq), base + MBOX_ICLR_REG);
41c0e939d   Kaihua Zhong   mailbox: Add supp...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  	return 0;
  }
  
  static int hi3660_mbox_unlock(struct mbox_chan *chan)
  {
  	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
  	unsigned int val, retry = 3;
  
  	do {
  		writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG);
  
  		val = readl(mbox->base + MBOX_IPC_LOCK_REG);
  		if (!val)
  			break;
  
  		udelay(10);
  	} while (retry--);
  
  	if (val)
  		dev_err(mbox->dev, "%s: failed to unlock mailbox
  ", __func__);
  
  	return (!val) ? 0 : -ETIMEDOUT;
  }
  
  static int hi3660_mbox_acquire_channel(struct mbox_chan *chan)
  {
  	unsigned long ch = (unsigned long)chan->con_priv;
  	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
  	struct hi3660_chan_info *mchan = &mbox->mchan[ch];
  	void __iomem *base = MBOX_BASE(mbox, ch);
  	unsigned int val, retry;
  
  	for (retry = 10; retry; retry--) {
  		/* Check if channel is in idle state */
  		if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) {
  			writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
  
  			/* Check ack bit has been set successfully */
  			val = readl(base + MBOX_SRC_REG);
  			if (val & BIT(mchan->ack_irq))
  				break;
  		}
  	}
  
  	if (!retry)
  		dev_err(mbox->dev, "%s: failed to acquire channel
  ", __func__);
  
  	return retry ? 0 : -ETIMEDOUT;
  }
  
  static int hi3660_mbox_startup(struct mbox_chan *chan)
  {
  	int ret;
41c0e939d   Kaihua Zhong   mailbox: Add supp...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  	ret = hi3660_mbox_unlock(chan);
  	if (ret)
  		return ret;
  
  	ret = hi3660_mbox_acquire_channel(chan);
  	if (ret)
  		return ret;
  
  	return 0;
  }
  
  static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg)
  {
  	unsigned long ch = (unsigned long)chan->con_priv;
  	struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
  	struct hi3660_chan_info *mchan = &mbox->mchan[ch];
  	void __iomem *base = MBOX_BASE(mbox, ch);
  	u32 *buf = msg;
  	unsigned int i;
2e4ac7cc4   Kevin Wangtao   mailbox: Hi3660: ...
182
  	int ret;
41c0e939d   Kaihua Zhong   mailbox: Add supp...
183

2e4ac7cc4   Kevin Wangtao   mailbox: Hi3660: ...
184
185
186
  	ret = hi3660_mbox_check_state(chan);
  	if (ret)
  		return ret;
41c0e939d   Kaihua Zhong   mailbox: Add supp...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  
  	/* Clear mask for destination interrupt */
  	writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG);
  
  	/* Config destination for interrupt vector */
  	writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG);
  
  	/* Automatic acknowledge mode */
  	writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG);
  
  	/* Fill message data */
  	for (i = 0; i < MBOX_MSG_LEN; i++)
  		writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4);
  
  	/* Trigger data transferring */
  	writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG);
  	return 0;
  }
b5452838c   Julia Lawall   mailbox: hi3660: ...
205
  static const struct mbox_chan_ops hi3660_mbox_ops = {
41c0e939d   Kaihua Zhong   mailbox: Add supp...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  	.startup	= hi3660_mbox_startup,
  	.send_data	= hi3660_mbox_send_data,
  };
  
  static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller,
  					   const struct of_phandle_args *spec)
  {
  	struct hi3660_mbox *mbox = to_hi3660_mbox(controller);
  	struct hi3660_chan_info *mchan;
  	unsigned int ch = spec->args[0];
  
  	if (ch >= MBOX_CHAN_MAX) {
  		dev_err(mbox->dev, "Invalid channel idx %d
  ", ch);
  		return ERR_PTR(-EINVAL);
  	}
  
  	mchan = &mbox->mchan[ch];
  	mchan->dst_irq = spec->args[1];
  	mchan->ack_irq = spec->args[2];
  
  	return &mbox->chan[ch];
  }
  
  static const struct of_device_id hi3660_mbox_of_match[] = {
  	{ .compatible = "hisilicon,hi3660-mbox", },
  	{},
  };
  
  MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match);
  
  static int hi3660_mbox_probe(struct platform_device *pdev)
  {
  	struct device *dev = &pdev->dev;
  	struct hi3660_mbox *mbox;
  	struct mbox_chan *chan;
  	struct resource *res;
  	unsigned long ch;
  	int err;
  
  	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
  	if (!mbox)
  		return -ENOMEM;
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	mbox->base = devm_ioremap_resource(dev, res);
  	if (IS_ERR(mbox->base))
  		return PTR_ERR(mbox->base);
  
  	mbox->dev = dev;
  	mbox->controller.dev = dev;
  	mbox->controller.chans = mbox->chan;
  	mbox->controller.num_chans = MBOX_CHAN_MAX;
  	mbox->controller.ops = &hi3660_mbox_ops;
  	mbox->controller.of_xlate = hi3660_mbox_xlate;
  
  	/* Initialize mailbox channel data */
  	chan = mbox->chan;
  	for (ch = 0; ch < MBOX_CHAN_MAX; ch++)
  		chan[ch].con_priv = (void *)ch;
e73cb83c8   Thierry Reding   mailbox: hi3660: ...
266
  	err = devm_mbox_controller_register(dev, &mbox->controller);
41c0e939d   Kaihua Zhong   mailbox: Add supp...
267
268
269
270
271
272
273
274
275
276
277
  	if (err) {
  		dev_err(dev, "Failed to register mailbox %d
  ", err);
  		return err;
  	}
  
  	platform_set_drvdata(pdev, mbox);
  	dev_info(dev, "Mailbox enabled
  ");
  	return 0;
  }
41c0e939d   Kaihua Zhong   mailbox: Add supp...
278
279
  static struct platform_driver hi3660_mbox_driver = {
  	.probe  = hi3660_mbox_probe,
41c0e939d   Kaihua Zhong   mailbox: Add supp...
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
  	.driver = {
  		.name = "hi3660-mbox",
  		.of_match_table = hi3660_mbox_of_match,
  	},
  };
  
  static int __init hi3660_mbox_init(void)
  {
  	return platform_driver_register(&hi3660_mbox_driver);
  }
  core_initcall(hi3660_mbox_init);
  
  static void __exit hi3660_mbox_exit(void)
  {
  	platform_driver_unregister(&hi3660_mbox_driver);
  }
  module_exit(hi3660_mbox_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller");
  MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");