Commit | Line | Data |
---|---|---|
8fbbfd96 MB |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * rWTM BIU Mailbox driver for Armada 37xx | |
4 | * | |
b37c3848 | 5 | * Author: Marek Behún <kabel@kernel.org> |
8fbbfd96 MB |
6 | */ |
7 | ||
8 | #include <linux/device.h> | |
9 | #include <linux/interrupt.h> | |
10 | #include <linux/io.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/mailbox_controller.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/armada-37xx-rwtm-mailbox.h> | |
17 | ||
18 | #define DRIVER_NAME "armada-37xx-rwtm-mailbox" | |
19 | ||
20 | /* relative to rWTM BIU Mailbox Registers */ | |
21 | #define RWTM_MBOX_PARAM(i) (0x0 + ((i) << 2)) | |
22 | #define RWTM_MBOX_COMMAND 0x40 | |
23 | #define RWTM_MBOX_RETURN_STATUS 0x80 | |
24 | #define RWTM_MBOX_STATUS(i) (0x84 + ((i) << 2)) | |
25 | #define RWTM_MBOX_FIFO_STATUS 0xc4 | |
26 | #define FIFO_STS_RDY 0x100 | |
27 | #define FIFO_STS_CNTR_MASK 0x7 | |
28 | #define FIFO_STS_CNTR_MAX 4 | |
29 | ||
30 | #define RWTM_HOST_INT_RESET 0xc8 | |
31 | #define RWTM_HOST_INT_MASK 0xcc | |
32 | #define SP_CMD_COMPLETE BIT(0) | |
33 | #define SP_CMD_QUEUE_FULL_ACCESS BIT(17) | |
34 | #define SP_CMD_QUEUE_FULL BIT(18) | |
35 | ||
36 | struct a37xx_mbox { | |
37 | struct device *dev; | |
38 | struct mbox_controller controller; | |
39 | void __iomem *base; | |
40 | int irq; | |
41 | }; | |
42 | ||
43 | static void a37xx_mbox_receive(struct mbox_chan *chan) | |
44 | { | |
45 | struct a37xx_mbox *mbox = chan->con_priv; | |
46 | struct armada_37xx_rwtm_rx_msg rx_msg; | |
47 | int i; | |
48 | ||
49 | rx_msg.retval = readl(mbox->base + RWTM_MBOX_RETURN_STATUS); | |
50 | for (i = 0; i < 16; ++i) | |
51 | rx_msg.status[i] = readl(mbox->base + RWTM_MBOX_STATUS(i)); | |
52 | ||
53 | mbox_chan_received_data(chan, &rx_msg); | |
54 | } | |
55 | ||
56 | static irqreturn_t a37xx_mbox_irq_handler(int irq, void *data) | |
57 | { | |
58 | struct mbox_chan *chan = data; | |
59 | struct a37xx_mbox *mbox = chan->con_priv; | |
60 | u32 reg; | |
61 | ||
62 | reg = readl(mbox->base + RWTM_HOST_INT_RESET); | |
63 | ||
64 | if (reg & SP_CMD_COMPLETE) | |
65 | a37xx_mbox_receive(chan); | |
66 | ||
67 | if (reg & (SP_CMD_QUEUE_FULL_ACCESS | SP_CMD_QUEUE_FULL)) | |
68 | dev_err(mbox->dev, "Secure processor command queue full\n"); | |
69 | ||
70 | writel(reg, mbox->base + RWTM_HOST_INT_RESET); | |
71 | if (reg) | |
72 | mbox_chan_txdone(chan, 0); | |
73 | ||
74 | return reg ? IRQ_HANDLED : IRQ_NONE; | |
75 | } | |
76 | ||
77 | static int a37xx_mbox_send_data(struct mbox_chan *chan, void *data) | |
78 | { | |
79 | struct a37xx_mbox *mbox = chan->con_priv; | |
80 | struct armada_37xx_rwtm_tx_msg *msg = data; | |
81 | int i; | |
82 | u32 reg; | |
83 | ||
84 | if (!data) | |
85 | return -EINVAL; | |
86 | ||
87 | reg = readl(mbox->base + RWTM_MBOX_FIFO_STATUS); | |
88 | if (!(reg & FIFO_STS_RDY)) | |
89 | dev_warn(mbox->dev, "Secure processor not ready\n"); | |
90 | ||
91 | if ((reg & FIFO_STS_CNTR_MASK) >= FIFO_STS_CNTR_MAX) { | |
92 | dev_err(mbox->dev, "Secure processor command queue full\n"); | |
93 | return -EBUSY; | |
94 | } | |
95 | ||
96 | for (i = 0; i < 16; ++i) | |
97 | writel(msg->args[i], mbox->base + RWTM_MBOX_PARAM(i)); | |
98 | writel(msg->command, mbox->base + RWTM_MBOX_COMMAND); | |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
103 | static int a37xx_mbox_startup(struct mbox_chan *chan) | |
104 | { | |
105 | struct a37xx_mbox *mbox = chan->con_priv; | |
106 | u32 reg; | |
107 | int ret; | |
108 | ||
109 | ret = devm_request_irq(mbox->dev, mbox->irq, a37xx_mbox_irq_handler, 0, | |
110 | DRIVER_NAME, chan); | |
111 | if (ret < 0) { | |
112 | dev_err(mbox->dev, "Cannot request irq\n"); | |
113 | return ret; | |
114 | } | |
115 | ||
116 | /* enable IRQ generation */ | |
117 | reg = readl(mbox->base + RWTM_HOST_INT_MASK); | |
118 | reg &= ~(SP_CMD_COMPLETE | SP_CMD_QUEUE_FULL_ACCESS | SP_CMD_QUEUE_FULL); | |
119 | writel(reg, mbox->base + RWTM_HOST_INT_MASK); | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | static void a37xx_mbox_shutdown(struct mbox_chan *chan) | |
125 | { | |
126 | u32 reg; | |
127 | struct a37xx_mbox *mbox = chan->con_priv; | |
128 | ||
129 | /* disable interrupt generation */ | |
130 | reg = readl(mbox->base + RWTM_HOST_INT_MASK); | |
131 | reg |= SP_CMD_COMPLETE | SP_CMD_QUEUE_FULL_ACCESS | SP_CMD_QUEUE_FULL; | |
132 | writel(reg, mbox->base + RWTM_HOST_INT_MASK); | |
133 | ||
134 | devm_free_irq(mbox->dev, mbox->irq, chan); | |
135 | } | |
136 | ||
137 | static const struct mbox_chan_ops a37xx_mbox_ops = { | |
138 | .send_data = a37xx_mbox_send_data, | |
139 | .startup = a37xx_mbox_startup, | |
140 | .shutdown = a37xx_mbox_shutdown, | |
141 | }; | |
142 | ||
143 | static int armada_37xx_mbox_probe(struct platform_device *pdev) | |
144 | { | |
145 | struct a37xx_mbox *mbox; | |
8fbbfd96 MB |
146 | struct mbox_chan *chans; |
147 | int ret; | |
148 | ||
149 | mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); | |
150 | if (!mbox) | |
151 | return -ENOMEM; | |
152 | ||
153 | /* Allocated one channel */ | |
154 | chans = devm_kzalloc(&pdev->dev, sizeof(*chans), GFP_KERNEL); | |
155 | if (!chans) | |
156 | return -ENOMEM; | |
157 | ||
34efc837 | 158 | mbox->base = devm_platform_ioremap_resource(pdev, 0); |
223a83bd | 159 | if (IS_ERR(mbox->base)) |
8fbbfd96 | 160 | return PTR_ERR(mbox->base); |
8fbbfd96 MB |
161 | |
162 | mbox->irq = platform_get_irq(pdev, 0); | |
223a83bd | 163 | if (mbox->irq < 0) |
8fbbfd96 | 164 | return mbox->irq; |
8fbbfd96 MB |
165 | |
166 | mbox->dev = &pdev->dev; | |
167 | ||
168 | /* Hardware supports only one channel. */ | |
169 | chans[0].con_priv = mbox; | |
170 | mbox->controller.dev = mbox->dev; | |
171 | mbox->controller.num_chans = 1; | |
172 | mbox->controller.chans = chans; | |
173 | mbox->controller.ops = &a37xx_mbox_ops; | |
174 | mbox->controller.txdone_irq = true; | |
175 | ||
2b983d12 | 176 | ret = devm_mbox_controller_register(mbox->dev, &mbox->controller); |
8fbbfd96 MB |
177 | if (ret) { |
178 | dev_err(&pdev->dev, "Could not register mailbox controller\n"); | |
179 | return ret; | |
180 | } | |
181 | ||
182 | platform_set_drvdata(pdev, mbox); | |
183 | return ret; | |
184 | } | |
185 | ||
8fbbfd96 MB |
186 | |
187 | static const struct of_device_id armada_37xx_mbox_match[] = { | |
188 | { .compatible = "marvell,armada-3700-rwtm-mailbox" }, | |
189 | { }, | |
190 | }; | |
191 | ||
192 | MODULE_DEVICE_TABLE(of, armada_37xx_mbox_match); | |
193 | ||
194 | static struct platform_driver armada_37xx_mbox_driver = { | |
195 | .probe = armada_37xx_mbox_probe, | |
8fbbfd96 MB |
196 | .driver = { |
197 | .name = DRIVER_NAME, | |
198 | .of_match_table = armada_37xx_mbox_match, | |
199 | }, | |
200 | }; | |
201 | ||
202 | module_platform_driver(armada_37xx_mbox_driver); | |
203 | ||
204 | MODULE_LICENSE("GPL v2"); | |
205 | MODULE_DESCRIPTION("rWTM BIU Mailbox driver for Armada 37xx"); | |
b37c3848 | 206 | MODULE_AUTHOR("Marek Behun <kabel@kernel.org>"); |