xref: /linux/drivers/mailbox/exynos-mailbox.c (revision 7db28abbea0f7dc1ec4fdfdc149db5fbd9e4c994)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2020 Samsung Electronics Co., Ltd.
4  * Copyright 2020 Google LLC.
5  * Copyright 2024 Linaro Ltd.
6  */
7 
8 #include <linux/bitops.h>
9 #include <linux/bits.h>
10 #include <linux/clk.h>
11 #include <linux/io.h>
12 #include <linux/mailbox_controller.h>
13 #include <linux/mailbox/exynos-message.h>
14 #include <linux/module.h>
15 #include <linux/of.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
18 
19 #define EXYNOS_MBOX_INTMR0		0x28	/* Interrupt Mask Register 0 */
20 #define EXYNOS_MBOX_INTGR1		0x40	/* Interrupt Generation Register 1 */
21 
22 #define EXYNOS_MBOX_INTMR0_MASK		GENMASK(15, 0)
23 #define EXYNOS_MBOX_INTGR1_MASK		GENMASK(15, 0)
24 
25 #define EXYNOS_MBOX_CHAN_COUNT		HWEIGHT32(EXYNOS_MBOX_INTGR1_MASK)
26 
27 #define EXYNOS850_MBOX_INTGR0		0x8	/* Interrupt Generation Register 0	*/
28 #define EXYNOS850_MBOX_INTMR1		0x24	/* Interrupt Mask Register 1		*/
29 
30 #define EXYNOS850_MBOX_INTMR1_MASK	GENMASK(15, 0)
31 #define EXYNOS850_MBOX_INTGR0_MASK	GENMASK(31, 16)
32 
33 #define EXYNOS850_MBOX_CHAN_COUNT	HWEIGHT32(EXYNOS850_MBOX_INTGR0_MASK)
34 
35 /**
36  * struct exynos_mbox_driver_data - platform-specific mailbox configuration.
37  * @intgr:		offset to the IRQ generation register, doorbell
38  *			to APM co-processor.
39  * @intgr_shift:	shift to apply to the value written to IRQ generation
40  *			register.
41  * @intmr:		offset to the IRQ mask register.
42  * @intmr_mask:		value to write to the mask register to mask out all
43  *			interrupts.
44  * @num_chans:		number of channels the mailbox can support (hardware
45  *			capability).
46  */
47 struct exynos_mbox_driver_data {
48 	u32 intgr;
49 	u32 intgr_shift;
50 	u32 intmr;
51 	u32 intmr_mask;
52 	int num_chans;
53 };
54 
55 /**
56  * struct exynos_mbox - driver's private data.
57  * @regs:	mailbox registers base address.
58  * @mbox:	pointer to the mailbox controller.
59  * @data:	pointer to driver platform-specific data.
60  */
61 struct exynos_mbox {
62 	void __iomem *regs;
63 	struct mbox_controller *mbox;
64 	const struct exynos_mbox_driver_data *data;
65 };
66 
67 static const struct exynos_mbox_driver_data exynos850_mbox_data = {
68 	.intgr = EXYNOS850_MBOX_INTGR0,
69 	.intgr_shift = 16,
70 	.intmr = EXYNOS850_MBOX_INTMR1,
71 	.intmr_mask = EXYNOS850_MBOX_INTMR1_MASK,
72 	.num_chans = EXYNOS850_MBOX_CHAN_COUNT,
73 };
74 
75 static const struct exynos_mbox_driver_data exynos_gs101_mbox_data = {
76 	.intgr = EXYNOS_MBOX_INTGR1,
77 	.intgr_shift = 0,
78 	.intmr = EXYNOS_MBOX_INTMR0,
79 	.intmr_mask = EXYNOS_MBOX_INTMR0_MASK,
80 	.num_chans = EXYNOS_MBOX_CHAN_COUNT,
81 };
82 
83 static int exynos_mbox_send_data(struct mbox_chan *chan, void *data)
84 {
85 	struct device *dev = chan->mbox->dev;
86 	struct exynos_mbox *exynos_mbox = dev_get_drvdata(dev);
87 	struct exynos_mbox_msg *msg = data;
88 
89 	if (msg->chan_id >= exynos_mbox->mbox->num_chans) {
90 		dev_err(dev, "Invalid channel ID %d\n", msg->chan_id);
91 		return -EINVAL;
92 	}
93 
94 	if (msg->chan_type != EXYNOS_MBOX_CHAN_TYPE_DOORBELL) {
95 		dev_err(dev, "Unsupported channel type [%d]\n", msg->chan_type);
96 		return -EINVAL;
97 	}
98 
99 	/* Ring the doorbell */
100 	writel(BIT(msg->chan_id) << exynos_mbox->data->intgr_shift,
101 	       exynos_mbox->regs + exynos_mbox->data->intgr);
102 
103 	return 0;
104 }
105 
106 static const struct mbox_chan_ops exynos_mbox_chan_ops = {
107 	.send_data = exynos_mbox_send_data,
108 };
109 
110 static struct mbox_chan *exynos_mbox_of_xlate(struct mbox_controller *mbox,
111 					      const struct of_phandle_args *sp)
112 {
113 	int i;
114 
115 	if (sp->args_count != 0)
116 		return ERR_PTR(-EINVAL);
117 
118 	/*
119 	 * Return the first available channel. When we don't pass the
120 	 * channel ID from device tree, each channel populated by the driver is
121 	 * just a software construct or a virtual channel. We use 'void *data'
122 	 * in send_data() to pass the channel identifiers.
123 	 */
124 	for (i = 0; i < mbox->num_chans; i++)
125 		if (mbox->chans[i].cl == NULL)
126 			return &mbox->chans[i];
127 	return ERR_PTR(-EINVAL);
128 }
129 
130 static const struct of_device_id exynos_mbox_match[] = {
131 	{
132 		.compatible = "google,gs101-mbox",
133 		.data = &exynos_gs101_mbox_data
134 	},
135 	{
136 		.compatible = "samsung,exynos850-mbox",
137 		.data = &exynos850_mbox_data
138 	},
139 	{},
140 };
141 MODULE_DEVICE_TABLE(of, exynos_mbox_match);
142 
143 static int exynos_mbox_probe(struct platform_device *pdev)
144 {
145 	const struct exynos_mbox_driver_data *data;
146 	struct device *dev = &pdev->dev;
147 	struct exynos_mbox *exynos_mbox;
148 	struct mbox_controller *mbox;
149 	struct mbox_chan *chans;
150 	struct clk *pclk;
151 
152 	data = device_get_match_data(&pdev->dev);
153 	if (!data)
154 		return -ENODEV;
155 
156 	exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL);
157 	if (!exynos_mbox)
158 		return -ENOMEM;
159 
160 	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
161 	if (!mbox)
162 		return -ENOMEM;
163 
164 	chans = devm_kcalloc(dev, data->num_chans, sizeof(*chans), GFP_KERNEL);
165 	if (!chans)
166 		return -ENOMEM;
167 
168 	exynos_mbox->regs = devm_platform_ioremap_resource(pdev, 0);
169 	if (IS_ERR(exynos_mbox->regs))
170 		return PTR_ERR(exynos_mbox->regs);
171 
172 	pclk = devm_clk_get_enabled(dev, "pclk");
173 	if (IS_ERR(pclk))
174 		return dev_err_probe(dev, PTR_ERR(pclk),
175 				     "Failed to enable clock.\n");
176 
177 	exynos_mbox->data = data;
178 	mbox->num_chans = data->num_chans;
179 	mbox->chans = chans;
180 	mbox->dev = dev;
181 	mbox->ops = &exynos_mbox_chan_ops;
182 	mbox->of_xlate = exynos_mbox_of_xlate;
183 
184 	exynos_mbox->mbox = mbox;
185 
186 	platform_set_drvdata(pdev, exynos_mbox);
187 
188 	/* Mask out all interrupts. We support just polling channels for now. */
189 	writel(data->intmr_mask, exynos_mbox->regs + data->intmr);
190 
191 	return devm_mbox_controller_register(dev, mbox);
192 }
193 
194 static struct platform_driver exynos_mbox_driver = {
195 	.probe	= exynos_mbox_probe,
196 	.driver	= {
197 		.name = "exynos-acpm-mbox",
198 		.of_match_table	= exynos_mbox_match,
199 	},
200 };
201 module_platform_driver(exynos_mbox_driver);
202 
203 MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@linaro.org>");
204 MODULE_DESCRIPTION("Samsung Exynos mailbox driver");
205 MODULE_LICENSE("GPL");
206