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
exynos_mbox_send_data(struct mbox_chan * chan,void * data)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
exynos_mbox_of_xlate(struct mbox_controller * mbox,const struct of_phandle_args * sp)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
exynos_mbox_probe(struct platform_device * pdev)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